home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Collection of Tools & Utilities
/
Collection of Tools and Utilities.iso
/
basic
/
qbnws202.zip
/
QBNWS202.NWS
< prev
next >
Wrap
Text File
|
1991-05-31
|
173KB
|
3,616 lines
Volume 2, Number 2 May 31, 1991
**************************************************
* *
* QBNews *
* *
* International QuickBASIC Electronic *
* Newsleter *
* *
* Dedicated to promoting QuickBASIC around *
* the world *
* *
**************************************************
The QBNews is an electronic newsletter published by Clearware
Computing. It can be freely distributed providing NO CHARGE is charged
for distribution. The QBNews is copyrighted in full by Clearware
Computing. The authors hold the copyright to their individual
articles. All program code appearing in QBNews is released into the
public domain. You may do what you wish with the code except
copyright it. QBNews must be distributed whole and unmodified.
You can write The QBNews at:
The QBNews
P.O. Box 507
Sandy Hook, CT 06482
Copyright (c) 1991 by Clearware Computing.
The QBNews Page i
Volume 2, Number 2 May 31, 1991
----------------------------------------------------------------------
T A B L E O F C O N T E N T S
1. From the Editor's Desk
Exciting New Things Happening with BASIC ..................... 1
2. Ask The Doctor
How to Copy Files with QuickBASIC ............................ 2
3. Advertisement
PDQComm - Professional Communications for "Power Programmers" 5
4. View Print
BASICZen by John Richard De Palma ............................ 6
5. Swap Shop
Create Bar Charts in Text Mode with QB by David Rice ......... 12
Create Pie Charts with QB by David Rice ...................... 16
Add BASIC 7's DIR$ Routine to your QB Programs by Dave Cleary 20
6. The Tool Shed
A Look at Crescent Software's PDQComm by Jim Harre ........... 22
7. Who ya gonna call? CALL INTERRUPT
Working with the Rodent by Daniel R. Berry ................... 27
A Graphics Mouse Cursor Design System by Warren G. Lieuallen . 32
8. The QBNews Professional Library
Fast Screen Printing by Christy Gemmel ....................... 34
ROM-BIOS Video Services ...................................... 48
Using FastPrint with BASIC 7 ................................. 49
9. Power Programming
An Improved Cloning Algorithm by Larry Stone ................. 51
10. New and Noteworthy
Custom Control Factory for Microsoft Visual Basic by Desaware 53
11. Fun and Games
ASCII Art -- A Piece of Computer History by Charles Graham ... 54
12. QBNews Special Report
Special Preview of Visual Basic by Dave Cleary ............... 57
13. EOF
Receiving The QBNews ......................................... 59
Submitting Articles to The QBNews ............................ 60
The QBNews Page ii
Volume 2, Number 2 May 31, 1991
----------------------------------------------------------------------
F r o m t h e E d i t o r ' s D e s k
----------------------------------------------------------------------
Exciting New Things Happening with BASIC
Since the last QBNews, quite a bunch of exciting new things has
been happening with our favorite language. First and foremost,
Microsoft has finally released Visual Basic, its Windows Basic
programming environment. Also, BASICPro, a new magazine devoted to
BASIC programmers has been started. Finally, Microsoft is holding
a big developers conference for BASIC programmers in Washington at
the end of August. We will be able to meet the developers of
Microsoft's BASIC products and even Mr. Bill himself. I hope to see
some of you readers up there.
This newsletter will be remain devoted to QuickBASIC programming.
However, the next two issues will be covering Visual Basic as this
is a very exciting new product. There is a VB preview in this issue
and I hope for more in depth stuff for VB and other Windows BASIC
environments for the next issue.
BASICPro magazine is now two issues old. Although a bit pricey,
the magazine holds much promise to become the Dr. Dobbs of QuickBASIC.
If you haven't seen BASICPro, give them a call at 415-688-1808 to
order a trial subscription. Don't forget to tell them you heard of
BASICPro through The QBNews.
Finally, I am kicking off a new series of articles for The QBNews.
It is called The QBNews Professional Library. I am putting together
an assembly language library written by the industries top
programmers. Not only will you get ASM source code of the routines
contained in the library, but you will get an in depth article
explaining what is going on. This series should help people understand
the bits and bytes that make our computers tick and help make them
into better programmers.
Dave Cleary - Publisher of The QBNews
The QBNews Page 1
Volume 2, Number 2 May 31, 1991
----------------------------------------------------------------------
A s k T h e D o c t o r
----------------------------------------------------------------------
Ask The Doctor
Ask The Doctor is a new question and answer area for The QBNews.
You send in your questions and I will try my best to either answer
them myself or get one of the many QB experts I know to answer them
for me. If your question is used, I will send you that issue of The
QBNews on disk as soon as it is published. Because I am not superman,
I will not be able to answer every ones questions so if you don't see
your question in the QBNews, don't expect a reply. Of course, you can
always call the Crescent Software Support BBS to leave me questions
that I will try to answer. That number is 203-426-5958.
Dear Dave:
Keep the QBNews coming. I have been doing simple programming in
QuickBasic for over a year now but have not been able to progress very
far. Your electronic magazine has been extremely helpful. Here is a
question you or one of your contributors might be able to help me
with. I am wanting to copy or move files around on my hard drive. I
do not want to shell to dos and use the copy command. How can I do
this using QB? I have been reading the disk I/O section of my QB
manual but it is not very clear about how I might go about copying or
moving files. I have look at some dos interrupt functions in a book
call, "Programmer's Guide to the IBM PC". But again I am not sure how
to go about using interrupts to copy or move files around.
Is there any help out there for a novice like me?
Thanks,
Steven E. Walker
Wilkinson, IN
Steven,
QuickBASIC has many different ways to read an write files. This may
have caused your confusion. Copying files is easily done in QuickBASIC
and is quite fast also.
Lets first look at the options QB gives us to read a file. The
three major commands to read a file are LINE INPUT, INPUT$, and GET.
LINE INPUT is only good for text files and is also one of the slowest
QB commands as far as file IO goes. That leaves INPUT$ and GET.
GET and PUT are by far the fastest IO commands QB has. This is
because with GET, the area of memory where the data gets placed is
already allocated. All QB has to do is set up some registers and then
call DOS to read the file.
INPUT$ is another command that works for both text and binary
The QBNews Page 2
Volume 2, Number 2 May 31, 1991
files. INPUT$ is slightly slower than the GET command because it
allocates memory to hold the data each time it is called. I have found
this speed difference to be inconsequential though. INPUT$ does offer
some advantages over GET in our CopyFile routine. When you are at the
end of a file, INPUT$ will return a string with the exact number of
bytes left in the file even if you asked for more. This relieves us of
knowing the size of the file we are working with. GET, on the other
hand, will pad your buffer with null characters, causing us to have to
truncate the last read if we want to keep our file lengths the same.
For this reason, I chose INPUT$ over GET to read in the file.
So here is our copy file routine:
DEFINT A-Z
DECLARE FUNCTION DIR$ (FileSpec$) 'Comment out if using BASIC 7
DECLARE FUNCTION CopyFile% (Source$, Dest$)
'$INCLUDE: 'QB.BI' 'Required for CALL INTERRUPT
CONST Block = 4096 'Set this to the length you
'want your buffer to be
'Example of how to call CopyFile
'Ercd = CopyFile("D:\PDQ\HISTORY.DOC", "C:\SCRAP\TEST1")
FUNCTION CopyFile% (Source$, Dest$) STATIC
DIM Regs AS RegType 'Needed for CALL INTERRUPT
'----- See if source file exists
IF LEN(DIR$(Source$)) = 0 THEN 'Use DIR$ function from this
'issues SwapShop if you are
'using QB 4.x.
CopyFile% = 1 'Source doesn't exist
EXIT FUNCTION 'Exit with error code
END IF
'----- See if destination exists
IF LEN(DIR$(Dest$)) THEN
CopyFile% = 2 'Destination already exists
EXIT FUNCTION 'Exit with error code
END IF
'----- Open files for BINARY
SFileNum = FREEFILE
OPEN Source$ FORBINARY AS #SFileNum
DFileNum = FREEFILE
OPEN Dest$ FOR BINARY AS #DFileNum
'----- Now copy the files over
DO
Buffer$ = INPUT$(Block, #SFileNum)
The QBNews Page 3
Volume 2, Number 2 May 31, 1991
PUT #DFileNum, , Buffer$
LOOP UNTIL EOF(SFileNum)
'----- Set the date and time of the copy to that of the original
Regs.ax = &H5700
Regs.bx = FILEATTR(SFileNum, 2) 'This gets DOS's file handle
INTERRUPT &H21, Regs, Regs 'Get date and time of original
'----- Check for an error
IF (Regs.flags AND 1) THEN
CLOSE #SFileNum, #DFileNum 'Close the files
KILL Dest$ 'Kill our copy because something
CopyFile% = 3 'went wrong. Exit with error
EXIT FUNCTION
END IF
Regs.ax = &H5701
Regs.bx = FILEATTR(DFileNum, 2)
INTERRUPT &H21, Regs, Regs 'Set date and time of copy
'----- Check for an error
IF (Regs.flags AND 1) THEN
CLOSE #SFileNum, #DFileNum 'Close the files
KILL Dest$ 'Kill our copy because something
CopyFile% = 3 'went wrong. Exit with error
EXIT FUNCTION
END IF
CLOSE #SFileNum, #DFileNum 'All done
CopyFile% = 0 'Return with success
END FUNCTION
This routine returns a 0 if everything went all right. It will
return a 1 if the source file doesn't exist and a 2 if the destination
file does exist. It will return a 3 if something went wrong in setting
the copy's date and time to that of the original. This routine uses
BASIC 7 PDS DIR$ function. For those of you with QuickBASIC, I have
written a DIR$ function for you that appears in this issues SwapShop
section. The DIR$ I wrote works in much the same way that BASIC 7's
does.
If you have a question for the Doctor, please send them to:
Ask The Doctor
P.O. Box 507
Sandy Hook, CT 06482
The QBNews Page 4
Volume 2, Number 2 May 31, 1991
----------------------------------------------------------------------
A d v e r t i s e m e n t
----------------------------------------------------------------------
PDQComm - Professional Communications for "Power Programmers"
PDQComm contains both low-level and high-level routines that add
advanced communications capabilities to programs written using
Microsoft QuickBASIC version 4.0 or later, and BASIC 7 PDS. PDQComm
was originally designed to be used with our P.D.Q. replacement link
library, however, PDQComm is also ideal for use with regular BASIC
programs.
***** PDQComm is small and fast *****
All of the routines provided with PDQComm are extremely small, and
thus add very little code to your programs. The core routines for
creating a complete terminal program add less than 2500 bytes.
Compare that to the more than 12K which QuickBASIC adds, or perhaps
more important, to other communications libraries that are available!
***** PDQComm is easy to use *****
All of the PDQComm routines have been designed to emulate the syntax
of the QuickBASIC routines they replace as closely as possible.
PDQComm also takes advantage of the features available in QuickBASIC
4.0 and later. For example, to get a string of characters from the com
port, you would do the following:
QuickBASIC: PDQComm:
Work$ = INPUT$(LOC(1), #1) Work$ = ComInput$(ComLoc)
Compare that to what other communications libraries make you do:
CALL MhGetRecvStatus(Port, CharsInBuffer, ECode)
Work$ = SPACE$(100 + CharsInBuffer * 2)
CALL MhGetData(Port, Work$, Length, ECode)
Work$ = LEFT$(Work$, Length)
***** PDQComm supports high speed communications *****
While most communications libraries claim to support baud rates up to
115k, a standard serial port has problems keeping up. To insure error
free high-speed communications, PDQComm supports the NS16550A UART.
This is an advanced IC that contains an on chip buffer, allowing the
PC more time to receive incoming characters.
***** PDQComm comes with many bells and whistles *****
Also included is full emulation for the ANSI, DEC VT52 and VT100, Data
General D215, and generic terminal standards. These emulations can be
instructed to operate within a windowed area, and you can even have
multiple windows active at one time. PDQComm also comes with XModem
and ASCII file transfer routines. The PDQComm manual contains
extensive tutorial information explaining modems, serial cables,
specifying port parameters, and UARTs. All of the important Hayes
commands are described in detail, and each emulation includes a table
of codes that are recognized. PDQComm costs $99. For more information
or to order PDQComm, please call Crescent Software.
Crescent Software - Orders:800-35-BASIC - Tech Supt:203-438-5300
Crescent Software Support BBS:203-426-5958 - 2400,N,8,1
The QBNews Page 5
Volume 2, Number 2 May 31, 1991
----------------------------------------------------------------------
V i e w P r i n t
----------------------------------------------------------------------
BASICZen by John Richard De Palma
Red haired Sandra is the manager of the local Egghead
Software store. Gazing at her collection of software I said,
"Hi Sandra, Uh... can you show me what books and software you
have on learning to program in BASIC?"
"No, no...NOOOO... John, you want this!" Sandra said, as she
thrust an orange 10 pound box of manuals and disks into my arms
and gave me a beaming smile.
She went on, "I studied Pascal and "C" in college for TWO
years, no one, and I mean no one studies BASIC anymore, it's a
dead programming language." She laughed, "Just as dead as
learning Latin."
"Well, Uh," I shifted my feet for better support and put
down Borland's version of Turbo Pascal, "I studied Latin for two
years, and it's not all that dead," . "You see, Latin teaches
you to know intuitively many English prefixes, suffixes and many
of the Romance language verbs and nouns...." my voice trailed
off, even to me that sounded like an apology for spending two
years learning about BIG Julie and wars fought with catapults and
giant slingshots.
"Oh, don't be SILLY," Sandra said, "Here, if you don't like
that, buy this, its C ++ with OOP."
"I'm not even going to ask what "OOP" is, I said, just sell
me something in BASIC," I sighed.
"What KIND of basic programming do you want?" Sandra asked
briskly, swivelling around to check on her employees and
motioning to Brian to stop playing with the joystick and get back
to work.
"Well, hell, I DON'T KNOW, I just want to learn how to make
my own programs like Paul Somerson does. If BASIC is good enough
for him, it's good enough for me," my voice rising a half-octave.
I started looking around for the IBM utilities section in the
hopes of finding some box with basic BASIC information on it.
There was no question that I knew NOTHING about programming.
I was awkward and out of my depth. I knew nothing about
programming except that it had to be better than using batch
files to do things with MS-DOS. I was going to tell Sandra about
batch files. Tell her about all the batch file programs that I
had looked at that promised much and delivered little. I wanted
to tell her about batch techniques that did not allow input into
them except as parameters on the command line or by using the
The QBNews Page 6
Volume 2, Number 2 May 31, 1991
dopey "FOR" command or the klutzy "IF ERRORLEVEL" command. I
wanted to tell her I wanted to make colorful screens with
selections that could be input by cursor control. I wanted to be
able to change directories, do file sorts... I wanted to
understand how the computer worked and then tell it what to do.
Hell and again hell, I wanted to control the computer software.
Who's Paul Somerson?" Sandra queried. "Some computer
propeller head in Santa Monica?"
"Gad, Sandy, give me a break! Paul Somerson is the editor
of my favorite PC bible, "DOS Power Tools," he programs in BASIC.
Look...look, you have his book on your bookshelf right here.
Wait... waaait, I'll find the section and read it to you. Come
back here, Oh let Brian wait on that guy, this will only take a
minute."
I picked up the book, found the page and read from my hero
Paul[1],
"One of the nicest things about BASIC is that if you
suddenly find yourself with a problem BASIC can tackle, you can
load it, stumble your way through a program and emerge with a
solution a few minutes later. So maybe your program wasn't the
most elegant display of programming virtuosity; who cares as long
as it worked?"
Sandra went on, "Well shoot yourself...I mean suit yourself,
heh, heh, a little joke there. BASIC is dumb and slow. Learn
"C" or Pascal, I did when I went to UCLA. No one teaches that
dumb stuff." Sandra was very convincing and convinced.
Backed into a corner and now defending both Latin, a dead
language and BASIC a dead programming language I asked, "Well, if
that's true, let me ask you a couple of questions. Do you use a
computer...? You do. Do you use a computer to do applications,
spreadsheets and mathematics...? You do. Do you use ANY of the
Pascal and "C" you learned to help you to doing things with these
programs. You don't..? Why?"
Sandra went on to tell me how hard it was to keep up these
great skills she learned in college and that she really didn't
have the time to program, or the interest. She freely admitted
that though she studied programming for two years, she never used
it outside of class. She glanced at the clock, at the three
people questioning Brian all at the same time and gave me a book
called "Learn BASIC Now." She said as she walked away, "BASIC,
is too dumb, it's a wimpy language. You're wasting your time,
you'll be sorry. It's really not even a HIGH language."
Apparently I bought a peasant computer language of limited
capacity for limited minds. If I wanted to be part of the
intelligentsia, I should program in "C". At least in "C" if not
in C ++ with OOP or in Pascal. So I went home, loaded the
The QBNews Page 7
Volume 2, Number 2 May 31, 1991
software and wrote my first BASIC program with Microsoft's Quick
Basic 4.5 Interpreter. The program was one line of text which
printed to the screen. Big deal, I want power and I get a batch
file look alike.
If I couldn't learn BASIC how could I learn these more
elitist and complicated computer languages? I needed some
verification and clarification. I began asking my friends about
computer programming.
Harry said, "Gosh John, I learned FORTRAN and COBOL when I
was 17, wrote flocks of programs in them, nope don't know BASIC,
it's too dumb and slow. What's that...do I ever USE FORTRAN and
COBOL? No, not in years. What good was learning it then? What
the !@#$%*!, kind of question is that!"
Harry is always a little sensitive if you imply that he
might be bragging. Harry is a card carrying elitist, he wouldn't
be caught dead using such a peasant computer language as BASIC.
Ray is different. Ray owns his own manufacturing company
and has three Phd.s', a law degree, and went to medical school
for three years. "Of course I can program in BASIC, John, don't
be silly, that's child's play. But don't get too technical, it's
been several years now, Hee Hee..., Ray chuckled.
"Well Ray, that's great, I'm having a dickens of a time, I
didn't realize that there was BASIC, BASICA, GWBASIC, PDB, and
QUICK BASIC. What do all of these names mean and which BASIC
should I learn?" I asked naively.
Ray sputtered a fine spray just as he was tasting the wine.
He ordered another bottle of Petite Sirah; and we were able to
finish dinner with that question hanging like still smoke in the
air.
So it went on, if they did program "in the higher languages"
of C, C +, Pascal they couldn't tell what and how they did the
programming.
"Well John why do you REALLY want to learn to program for,
comm' on, tell me....comm' on...tell the truth," Marvin asked.
Marvin programs in "C" and does programs in artificial
intelligence and makes jokes about "the artificial
intelligentsia."
In desperation, I asked Marvin to write me a program that
could be an all purpose tool, sort of a Swiss Army knife that
would put up menus, take direct input from the keyboard, let you
pick your colors, be user friendly, be modifiable, you know like
software should be. Marvin said that I didn't really know what I
wanted or, I wanted too much. Besides nobody programs in BASIC.
So I went home and dragged out QUICK BASIC again and tried
The QBNews Page 8
Volume 2, Number 2 May 31, 1991
halfheartedly to learn something that no one knew about from
books written by REAL "propeller heads." I read and reread the
texts trying to UNDERSTAND what the writer was driving at.
Unfortunately BASIC is mainly written by programmers who can
write code but who can't write to communicate with humans.
It was a sort of Zen, reading and not understanding. It was
a sort of chant. Reading again and again such stuff as: "DATE$
Statement sets the current date," and "DATE$ Function returns a
string containing the current date," and "FUNCTION Statement
Declares the name, parameters, and the code that form the body of
a FUNCTION procedure.[2]" Well that is as clear as Zen, and like
Zen you have to have a FEEL for the terms. As any Zen master
will tell you once you have the answer to the question, you DON'T
have the answer.
QUICK BASIC is Zen, a doing without knowing. But I
followed the instructions ---cook book style--- and a program
could be made to do something. The sound of one hand clapping
makes sense now. Trying to understand what is the meaning of the
phrase, "What is the sound of one hand clapping?" is no more
difficult than trying to understand books written by programmers.
I would have given up too, except I was given a QUICK BASIC
program that did something that I needed to have done. Pete
programs in QUICK BASIC. Pete is probably the only person I know
that REALLY programs anything for himself and he uses QUICK
BASIC. We have a mutual interest and problem with some data
collection and analysis. Pete had an answer to the problem and
he had a real program that would give an answer all written in
QUICK BASIC.
"Now Pete, I WON'T steal this program. Also, I won't sell
this program and make a million dollars on it (Well... at least
not without giving you HALF). Yes, I promise, yes that's right,
cross my heart and hope to die. And I won't give it to the
Iraqis! Now will you please...please....PLEASE give me a copy to
take home?"
After whining and pleading that I would not sell his first
born program into slavery or copyright it, he gave me a copy.
That is another Zen portion of programming, you have to earn the
knowledge yourself, no one can do it for you. Only with
programmers it's worse than Zen, they won't give you a copy of
what they know! I watched him pull up the file, run it through
his compiler and give me code that would run by its self. It was
like watching someone start a fire by using an ancient ritual, by
using a bow and a stick. It was the dawn of civilization, the
passing of knowledge, the starting of fire by friction. I was
given a real stand-alone executable program written by a real
person, Wow! After more whining he capitulated completely and
gave me the SECOND file, the QUICK BASIC code file.
I put the diskette in my shirt pocket, it was too important
The QBNews Page 9
Volume 2, Number 2 May 31, 1991
to place it anywhere's else. That night I ran it inside of my
QUICK BASIC compiler. Gadsooks! it worked! The damn thing
calculated and printed the results out lightening fast and it was
information that I could really use.
Zen, part two, you can't learn something you have no use
for. That's what Sandra, Harry, Ray and all the others were
talking about. They wrote programs in class on problems that
they were given, not on problems they wanted solutions for
themselves. That's why learning programming is like Zen, it is
meaningless unless you have some use for the knowledge (which is
both very much like and UNLIKE Zen).
Good ole Paul Somerson was right. First, you need a project
that you really...really want to do. Then use the books to look
up the procedures to do the project with. Just learning all 190
QUICK BASIC commands won't cut it. You have to use it ...or lose
it!
I went back to Egghead Software; Sandra and Brian had moved
on. Scott and Lance programmed in Pascal. I asked them if there
was anything new in QUICK BASIC that was fun. Lance gave me
Microsoft's GAMESHOP. It came with the same book that I already
had, but the software contained 6 games which could be run inside
of QUICK BASIC, the code could be examined. With much head
scratching and replaying you could actually figure out how the
programmers did what they did. Again, like Zen you must
persevere, be tested, try and fail, try and fail, knowledge
doesn't come easily. But everyone likes to play games, so it
wasn't all Zen.
That was a month ago, and though it is still slow going, I
am making progress. Pete and GAMESHOP gave me hope. I have
uploaded two programs to CompuServe as shareware. The first
program has attracted two dozen downloads in two weeks. Not
great, but a start and this is also Zen; you work and study long
for small (or no) rewards. I guess some modem users downloaded
the program because it was simple, colorful, and played a song.
Nothing grand, just a program called BIRTHDAY.ZIP that puts up
colored boxes on the screen, accepts user input, and plays "Happy
Birthday" if the computer clock reads the same day and month as
the ones you type in. If it's not your birthday, it flashes
different colors and plays "Happy Unbirthday."
Some one laughed when I played the program for them and
jokingly asked to see it display the EXACT age of anyone whose
birthday was not the day it was run. He also wanted something
that would distinguish if the person inputting the data was young
or old (over or under 21).
That was beyond my ability, but then I found, if you looked
hard enough, someone had already done some of these things in
QUICK BASIC or BASICA. I found a Julian (named after Big Julie
no less) calendar function which does just that, and added it to
The QBNews Page 10
Volume 2, Number 2 May 31, 1991
the program. After struggling to add that formula, it was easy
to figure out a "LOOP" that would change a phrase depending on
what the person's age was. Though the latter was simple math, it
had been years since I had been forced to do any thinking about
mathematics. Zen and math have a lot in common, but that is
another story.
With a program that calculated the person's exact age, every
young woman that played the program exclaimed "<Gasp>, that's
wrong I am NOT 29.078345 years old!" if that was her exact age.
I now warn women over 30 that this might be a traumatic event as
the computer will calculate their exact age, but they sail
blithely ahead, not believing that it will happen. All in all, a
lot of fun and some insight into human nature.
The second program, FOR-LISA.ZIP uses random number formulas
to generate screen colors, changes the screen to 40 characters
wide, and displays more ASCII graphics. This one plays a
Beethoven sonata and takes advantage of some great 1982 music
programming in BASICA that I found on a BBS. Again, I generated
simple mathematical formulas to do the work of many lines of
code. Another secret of programming which could only be
uncovered by doing. Zen is doing and not doing.
So, nothing sensational, but now my batch files are getting
a once over with this new knowledge. Now I realize that the
macros in Microsoft Word, WordPerfect, and the script in ProComm
Plus are written in BASIC. Now these macro formulas make sense!
There has been a mystic clarification of macros, again like Zen
what you learn affects other areas of knowledge.
I am thinking of ordering from Crescent Software[3] a QUICK
BASIC package that allows you to program mice, windows,
accounting, and databases. Now I have hope, and that also is
Zen. Yeah, nothing sensational unless you thought that BASICA
was another name for Zen and that "Real Men only program in C."
References:
(1) Somerson, Paul, PC Magazine Power Tools 2nd Edition,
Bantam Books, 1990 June;1157.
(2) Microsoft, Programming in QuickBASIC Version 4.5,
1988;270-1.
(3) Crescent Software, Inc; 32 Seventy Acres, West Redding,
Connecticut 06896; VOICE: 203-846-2500.
**********************************************************************
John Richard De Palma is a California physician who practices adult
internal-medicine. Though he is old enough to know better, he has
decided to study computers. All the conversations and facts in this
article are true. Only the names and locations have been changed to
protect the unknowing and innocent who talked to him. He can be
reached on Compuserve at 76076,571 or in care of this newsletter.
**********************************************************************
The QBNews Page 11
----------------------------------------------------------------------
S w a p S h o p
----------------------------------------------------------------------
'BARS.BAS by David Rice
' I've been working on a labor scheduling program for
' the past two years, in a manufacturing facility that
' builds an automated blood analyzer for hospitals. When an
' order is placed by a hospital for one of these units, the
' Planner must figure out if her or his manufacturing floor
' can handle the added hours in labor (Standard Hours or
' Demonstrated (actual) Hours), and if the added labor will
' be greater than what the floor can handle (Capacity
' Hours).
'
' The best and easiest way for the Planner to see this
' is by using a graph. In my scheduling program there is a
' spreadsheet to enter quantities (for each week, month,
' year, or quarter), for each assembly. A graph allows the
' Planner to see immediately where she or he has excess
' Capacity, so that assemblies may be scheduled during these
' slack periods. Ideally, Scheduled Actual Hours will meet
' Capacity, never go higher than Capacity, seldom below.
' With a graph, this is easy to see.
'
' In the sample code here, I've excluded the Capacity
' bar and just included the single data set, for simplicity.
' To be functional, the routine must be able to handle large
' numbers mixed with small ones, sizing bars in proportional
' to their original values. It must allow the programmer to
' select how much room to leave at the top of the chart for
' a title, and how much room to allow at the bottom for
' stuff like labels, comments, etc.
'
' I've used text mode and not graphics for several
' reasons. First is that almost any computer monitor will
' handle the graph. Also, if one wants to print out the
' graph, one just hits the print-screen button. Since the
' scheduling program was designed for a Novel Netware
' environment, various and vastly differing hardware may be
' used, and text mode allows the programmer to ignore the
' problem of different monitor types.
'
' So the sample code is presented here for your use.
' The method is extremely simple, works every time, and well
' tested. Since there's no point in everyone inventing the
' wheel, this code is being published in the QuickBASIC News
' letter.
'
'-------------------------- Cut Here ------------------------------
'
' BARS.BAS
' David Rice
' June 16, 1990
The QBNews Page 12
Volume 2, Number 2 May 31, 1991
'
' Define Everything As Integer For Speed
'
defint a-z
'
' Top.Row.Allowed is how high you want the
' bars to be on the screen. Bottom.Row.Allowed
' is how low you want the bars to be on the
' screen. This is to allow text to be placed
' on the screen where you wish, top or bottom.
'
How.Many.Observations = 12
Top.Row.Allowed = 1
Bottom.Row.Allowed = 25
'
' Value!() Will Hold The 12 Bar Values. 12
' Was Chosen For This Example Because It Spans
' One Year (I.e. Bars Represent Months). TOP%()
' will hold the top row on the screen to draw
' the bars.
'
Redim Value!(How.Many.Observations),Top%(How.Many.Observations)
'
' Read in from the DATA statement the sample
' values (called "Observations").
'
for a = 1 to How.Many.Observations
read value!(a)
next
'
data 132.3,532,53,123,433,86,445,335,122,134,505,234.74
'
' Some other sample values you may wish
' to draw. No matter what range the numbers
' are, the largest number will define how
' the bars will be drawn.
'
' data 10,20,30,40,50,60,70,80,90,100,110,120
' data 110,100,80,60,40,20,20,40,60,80,100,110
' data 999,1032,4343,4365,2033,2354,2335,2123,2102,325,3255,1212
'
' If you are using a direct screen writing
' utility such as QPRINT or FASTPRT, you'll
' want to convert the number into a string.
' Also, with the horizontal lines going across
' the screen, you'll not want leading or
' trailing spaces.
'
DEF FN INT.Value$(Value!)
defint a-z
'
' Round up value!
'
If Value! <= 32767 then Value! = cint(Value!)
The QBNews Page 13
Volume 2, Number 2 May 31, 1991
'
' Find how long the string will be.
'
Span% = (len(Str$(Value!)) - 1)
'
' Convert number to string, and remove
' the leading space.
'
XX$ = mid$(str$(Value!),1 - (Value! >=0))
FN INT.Value$ = xx$
END DEF
'
' To avoid dividing by zero later,
' assign the variable HIGH# a negligable
' value. This means that if you try to
' graph all observations of zero, no
' error will occur.
'
high# = 0.02
'
' Find The Highest Bar, and put it's value in HIGH#
'
for Bar = 1 to How.Many.Observations
if Value!(Bar) > high# then high# = Value!(bar)
next
'
' Define the highest bar in terms of
' screen rows. This could be a very small
' number when the values being graphed
' are large. The largest bar will span
' the screen from Top.Row.Allowed to
' Bottom.Row.Allowed, minus 1 for the
' value labels, and all other bars will
' be scaled using PERCENT# to this largest bar.
'
Percent# = ((Bottom.Row.Allowed - 1) / high#)
'
' Place horizontal lines on the screen.
' You may not want these, however. The
' next FOUR lines of code may be removed
' without causing problems elsewhere.
'
color 13,0,0
for row = Bottom.Row.Allowed to Top.Row.Allowed step -2
Locate row,2,0,0,0
print string$(78,196);
next
'
' Draw the observations. Start The Loop.
'
for Bar = 1 to How.Many.Observations
'
color 10,0,0
'
The QBNews Page 14
Volume 2, Number 2 May 31, 1991
' Define each value as a subset of the
' largest.
'
XX% = (Value!(Bar) * percent#)
'
' Convert to screen row value.
'
Top%(Bar) = Bottom.Row.Allowed - (xx - Top.Row.Allowed)
'
' Calculate the column on the screen.
'
col = (6 + (Bar - 1) * 6)
'
' Start at the highest row of the bar and
' fill down to the lowest row allowed.
'
for row = top%(Bar) to Bottom.Row.Allowed
'
' If the value is so small compared to the
' largest, it may be too small to draw on the
' screen. This much be checked for.
'
if row > 1 then
Locate row,col,0,0,0
'
' Print the bar. The characters may be changed
' of course to fit your particular needs. I've
' included some "commented-out" samples of
' different characters: to try them, put a
' squote in front of the first line, and remove
' the squote from the line you'd like to try.
'
print string$(4,219);
'print string$(4,178);
'print string$(4,176);
'
end if
next
'
' If you do not want the values printed at
' the top of the bar, remove the next FIVE
' executable lines that follow.
'
' Find the row to place the number.
'
Row = (Top%(Bar) - 1)
'
' If the bar is so small that the number
' would be placed lower than the bottom row
' allowed, place it on the bottom row allowed.
'
if Row > Bottom.Row.Allowed then row = Bottom.Row.Allowed
'
color 12,0,0
The QBNews Page 15
Volume 2, Number 2 May 31, 1991
Locate Row,Col,0,0,0
print FN INT.Value$(Value!(Bar));
next
'
' Pause for any key press. We're done!
'
while inkey$ = ""
wend
----------------------------------------------------------------------
'PIES.BAS by David Rice
' Make all numeric variables INTEGERS
'
defint a-z
'
' Random tile pattern function.
'
DEF FN Tile$
DefInt a-z
ti$ = ""
char = 0
Randomize timer
'
while char < 1
Randomize timer
char = ((8 * rnd) + 1)
wend
'
for a = 1 to char
Randomize timer
b = int((255 - 32 + 1) * RND + 32)
ti$ = ti$ + chr$(b)
next
fn tile$ = ti$
END DEF
'
' For reasons of demonstration, the number
' of observations (slices) in the pie chart
' have been randomized to a number from 2 to
' 12. The maximum number of slices has been
' set to 12, but if you can do without the
' legends that are placed on the left of the
' screen, you could make room for more slices.
'
start.here:
Observations% = (rnd * 12) + 1
if Observations% > 12 then Observations% = 12
if Observations% = 1 then Observations% = 2
'
' Set aside room for the arrays we need.
'
Redim Wedge!(Observations%),Degrees!(Observations%)
Redim Angle!(Observations%),tile$(Observations%)
Redim x%(Observations%),y%(Observations%)
Redim Diff!(Observations%)
'
The QBNews Page 16
Volume 2, Number 2 May 31, 1991
' Go to EGA mode, clear the screen, and
' set the background color to blue (attribute
' of 1). Attribute 15 looks good, too.
'
screen 8
cls
paint (3,3),1
'
' Get random tile patterns for all observations.
'
for tile% = 1 to Observations%
tile$(tile%) = fn tile$
next
'
' Find the total value for all observations.
' For this demonstration, these values have been
' randomized.
'
Total! = 0
for a = 1 to Observations%
'
Wedge!(a) = (777 * RND)
'
If Wedge!(a) < 1 then Wedge!(a) = 1
Total! = Total! + Wedge!(a)
next
'
' Scale everything down to a percent of a
' circle (360 degrees). Also draw the circle.
'
Pie.Attr% = 14
Perc! = (total! / 360)
circle (400,100),205,Pie.Attr%
'
' Calculate the direction to turn the line,
' from a starting direction of zero (straight
' up).
'
Direction! = 0
'
' For every observation calculate how many
' degrees of the circle the slice will cover,
' then the direction to turn.
'
for a = 1 to Observations%
Degrees!(a) = (Wedge!(a) / Perc!)
Direction! = Direction! + Degrees!(a)
'
' Move to the center of the pie, without
' drawing a line.
'
Draw "BM400,100"
'
' Turn the angle and then store this angle
The QBNews Page 17
Volume 2, Number 2 May 31, 1991
' for use later.
'
Draw "TA=" + Varptr$(Direction!)
Angle!(a) = Direction!
'
' Draw the slice.
'
Draw "U86"
next
'
' In order to fill the slice with a tile,
' we need to know the center of the slice.
' If we tried to PAINT outside the slice,
' we would get undesirable results, such as
' a screen full of trash.
'
' Calculate for every observation.
'
for a = 1 to Observations%
'
' If this iteration is the first, we
' start from zero, take the angle the slice
' was drawn, and find the middle between
' the two.
'
If A = 1 then
half! = (Angle!(1) / 2)
Diff!(1) = Half!
else
'
' For all other iterations, look for the
' distance between this slice and the previous
' one.
'
Half! = (ABS(Angle!(a) - Angle!(a - 1)) / 2) + Angle!(a - 1)
Diff!(a) = (ABS(Angle!(a) - Angle!(a - 1)) / 2)
end if
'
' Throw out a circle if there's more than one.
'
If Half! > 359.9999! then half! = half! - 360
'
' Go to the center of the pie, but do not draw
' any line.
'
Draw "BM400,100"
'
' Make our new course heading good.
'
Draw "TA=" + Varptr$(half!)
'
' Move but DO NOT DRAW up to a point
' almost but not quite to the circle's
' boundry.
The QBNews Page 18
Volume 2, Number 2 May 31, 1991
'
Draw "BU83"
'
' Store this position, then calculate
' the same for all the other observations.
'
x(a) = Point(0)
y(a) = Point(1)
next
'
for a = 1 to Observations%
'
' If the slice is too small to paint,
' do not do so. Otherwise, paint it with
' the proper tile.
'
If Diff!(a) > .5! then paint (X(a),Y(a)),tile$(a),Pie.Attr%
'
' Calculate the legend bubble position ROW.
'
Y = 10 + (A - 1) * 16
'
' Draw bubble and PAINT it with the same tile.
'
circle (80,y),16,Pie.Attr%
Paint (80,y),tile$(a),Pie.Attr%
'
' Calculate the percentage of the whole this
' slice is.
'
percent# = (wedge!(a) / total!) * 100
next
'
call get.1.chr(ii$)
'
' Like a tile pattern? Hit ALT/P
' and it will be saved in as a file.
'
If II$ = chr$(0) + chr$(25) then
Open "Tile.Asc" for output as #1
for a = 1 to observations%
'
print #1, using "Tile.Pattern$(\\) = ";mid$(str$(a),1 - (a >=0));
'
Size% = len(Tile$(a))
for b = 1 to (size% - 1)
print #1, using "CHR$(###) + ";asc(mid$(tile$(a),b,1));
next
print #1, using "CHR$(###)";asc(mid$(tile$(a),size%,1))
next
close
call get.1.chr(ii$)
end if
'
The QBNews Page 19
Volume 2, Number 2 May 31, 1991
if ii$ <> chr$(27) then goto start.here:
'
sub get.1.chr(i$) static
defint a-z
i$ = ""
while i$ = ""
i$ = inkey$
wend
end sub
----------------------------------------------------------------------
'DIR.BAS by Dave Cleary
'
'One of the most useful additions to BASIC 7 PDS is the DIR$ function.
'This function allows you to read a directory of filenames. It also
'allows you to check the existence of a file by doing the following:
'
' IF LEN(DIR$("COMMAND.COM")) THEN
' PRINT "File Found"
' ELSE
' PRINT "File not found"
' END IF
'
'Now QuickBASIC 4.X users can have this useful function for their
'programs.
'
'Calling DIR$ with a FileSpec$ returns the the name of the FIRST
'matching file name. Subsequent calls with a null FileSpec$ return the
'NEXT matching file name. If a null string is returned, then no more
'matching files were found. FileSpec$ can contain both a drive and a
'path plus DOS wildcards. Special care should be taken when using
'this on floppy drives because there is no check to see if the drive
'is ready.
DEFINT A-Z
DECLARE FUNCTION DIR$ (FileSpec$)
'$INCLUDE: 'QB.BI'
'----- Some constants that DIR$ uses
CONST DOS = &H21
CONST SetDTA = &H1A00, FindFirst = &H4E00, FindNext = &H4F00
'--------------------------------------------------------------------
'This shows how to call DIR$ to find all matching files
'CLS
'FileSpec$ = "C:\QB\SOURCE\*.BAS"
'Found$ = DIR$(FileSpec$)
'DO WHILE LEN(Found$)
' PRINT Found$
' Found$ = DIR$("")
'LOOP
'--------------------------------------------------------------------
The QBNews Page 20
Volume 2, Number 2 May 31, 1991
FUNCTION DIR$ (FileSpec$) STATIC
DIM DTA AS STRING * 44, Regs AS RegTypeX
Null$ = CHR$(0)
'----- Set up our own DTA so we don't destroy COMMAND$
Regs.AX = SetDTA 'Set DTA function
Regs.DX = VARPTR(DTA) 'DS:DX points to our DTA
Regs.DS = -1 'Use current value for DS
InterruptX DOS, Regs, Regs 'Do the interrupt
'----- Check to see if this is First or Next
IF LEN(FileSpec$) THEN 'FileSpec$ isn't null, so
'FindFirst
FileSpecZ$ = FileSpec$ + Null$ 'Make FileSpec$ into an ASCIIZ
'string
Regs.AX = FindFirst 'Perform a FindFirst
Regs.CX = 0 'Only look for normal files
Regs.DX = SADD(FileSpecZ$) 'DS:DX points to ASCIIZ file
Regs.DS = -1 'Use current DS
ELSE 'We have a null FileSpec$,
Regs.AX = FindNext 'so FindNext
END IF
InterruptX DOS, Regs, Regs 'Do the interrupt
'----- Return file name or null
IF Regs.Flags AND 1 THEN 'No files found
DIR$ = "" 'Return null string
ELSE
Null = INSTR(31, DTA, Null$) 'Get the filename found
DIR$ = MID$(DTA, 31, Null - 30) 'It's an ASCIIZ string starting
END IF 'at offset 30 of the DTA
END FUNCTION
The QBNews Page 21
Volume 2, Number 2 May 31, 1991
----------------------------------------------------------------------
T h e T o o l S h e d
----------------------------------------------------------------------
A Look at Crescent Software's PDQComm by Jim Harre
Product Review - PDQComm 2.50
Source: Crescent Software
32 Seventy Acres
West Redding, CT 06896
(203) 438-5300
Price: $99.00
PDQComm 2.50 is a communications add-on library written by Dave
Cleary (your illustrious QBNews publisher) and marketed by Crescent
Software. Strangely enough, PDQComm does work with PDQ -- as well as
QuickBasic 4.x and BPDS 7.x. Since PDQ doesn't provide access to the
communications ports as QB does, an add-on package is necessary for
serial work. PDQComm provides this feature.
PDQComm provides you with over 50 low and high level routines. A
number of the procedures have been lifted from Crescent's QuickPak
Pro and PDQ packages. The routines are small and speedy. In short,
it is a complete serial port programmmer's toolkit.
The documentation is absolutely first rate. Those of you upgrading
from version 2.0 are in for a very pleasant surprise. Besides the
usual excellent routine descriptions, a brief communications
tutorial (including a short section on the infamous baud vs. bps
controversy), connector pinouts, UART descriptions and register
info, and a brief summary of 'Standard' Hayes commands and S
registers are included at the end of the manual. If your desk looks
like mine, it's nice to have just ONE reference with just about
everything you need.
Crescent has switched from the old, thin, 'blah' beige covers to
attractively printed heavy card stock covers. This should help keep
your manuals from being floppier than your disks. The only drawback
to the manual (and this is a minor nit) is that they have changed
the binding from a plastic spine to one of those metal double spiral
bindings that get bent up easily and bind up the pages. At least,
you could stick a small label on the old spines - these new ones
don't work well for labeling.
Unlike some add-on libraries, PDQComm provides .QLB and .LIB files
for both QB and BPDS in one package. The source code is provided as
with all Crescent Products. A number of demonstration programs are
provided to acquaint you with using the routines, including a TSR
comm program.
Several new features are added in version 2.50 -
<> Two comm ports can be open simultaneously.
<> Ports with non-standard addresses or IRQs can be opened.
<> A function to determine the UART type has been added.
The QBNews Page 22
Volume 2, Number 2 May 31, 1991
<> You can enable/disable the FIFO buffer in 16550 UARTS.
<> You can adjust the receive buffer size 'on the fly'.
<> Grab status of CTS, DSR, RI, and DCD as a TYPE variable.
<> The timeout delay is adjustable in seconds when sending data.
<> Several routines from PDQ are now included.
QuickBasic and the Basic Professional Development System
(otherwise known as BPDS) already have communications support built
in -- so why would anyone buy an add-on package to do something that
Basic already does? For the same reasons you purchased QB or BPDS when
Basica or GW-Basic was included with your DOS -- functionality and
flexibility. One of the great failings of the routines furnished
with QB is that you need to use ON ERROR to trap errors. This
results in larger, slower code -- exactly what you don't need when
working with communications. QB also has a VERY nasty habit of
dropping Data Terminal Ready when you exit a program. If you are
writing a series of programs to be executed, this is usually deadly
to your modem connection since most modems will go onhook when you
lower DTR.
Over the last couple years that I've carried the QUIK_BAS echo on
my system, there has been a continuing discussion on the merits of
using a prewritten library of routines versus writing your own. It's
no secret that I often use and advocate third party libraries. For
me, the reason is simple - time is money. When you write code for a
living, your production rate tends to directly affect your wages at
salary review time. A hundred bucks for a comm package is a drop in
the bucket compared to how long it would take me to develop (and
especially debug) a similiar set of routines in assembly language.
In a commercial setting and especially a production environment,
downtime due to program 'quirks' is measured in thousands of
dollars. Things must be solid the first time out - there isn't much
time for playing around with it until you get it right. Over the
last year in production programs, PDQComm has proved to be extremely
stable and bug free.
Much of my work with PDQComm has been to build TSR "device drivers"
to communicate with digital scale indicators. Every one of these
rascals tends to work differently, while the main application
program needs to remain the same for the user. To avoid maintaining
a dozen similiar but different programs, the main program simply
issues an interrupt call and receives a numeric string from the
scale.
Before the program is launched, a small (8-9K) TSR driver is loaded
that handles the grunt work of maintaining a conversation with the
scale and responding to requests from the main program for
information. This TSR is compiled using PDQ and PDQComm. Each
different device has it's own driver that handles the peculiarities
of that scale and has a common output format. By having the driver
take over and handle an unused interrupt, all the main program needs
to know is that when it calls INT 61H, the scale data magically
appears - no matter what scale is attached. Because PDQComm can
create compact code and PDQ can create TSR's, what would have been a
The QBNews Page 23
Volume 2, Number 2 May 31, 1991
real drudge with ASM is now almost easy - in BASIC.
To give you an idea what programming with PDQComm feels like,
here's a small program and comments (lifted directly from the PDQComm
manual):
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Besides using a syntax as close to BASIC's as possible, PDQComm
also returns error information in the BASIC ERR function. This
lets you easily test the success or failure of the most recent
operations, without requiring ON ERROR. Unlike other communi-
cations libraries you may have seen, PDQComm uses the minimum
number of parameters possible. This greatly reduces the size of
your programs, and also improves their speed.
DEFINT A-Z 'all integers please
DECLARE SUB OpenCom (Action$) 'same as OPEN "COM..."
DECLARE SUB ComPrint (Work$) 'same as PRINT #n,
DECLARE SUB CloseCom () 'same as CLOSE #n
DECLARE FUNCTION BIOSInkey% () 'similiar toASC(INKEY$)
DECLARE FUNCTION ComEof% () 'same as EOF(n)
DECLARE FUNCTION ComInput$(NumChars) 'same as INPUT$(n,#n)
CALL OpenCom("COM1:2400,N,8,1,RB512,XON") 'open the port
IF ERR THEN 'oops
PRINT "Error opening the communications port."
END
END IF
DO
Char = BIOSInkey% 'get what was typed
IF Char = 27 THEN EXIT DO 'exit if it was Escape
IF Char THEN CALL ComPrint(CHR$(Char)) 'anything else, send it
IF NOT(ComEof%) THEN 'was anything received?
ComString$ = ComInput$(ComLoc%) 'yes, get the characters
PRINT ComString$; 'print them on the screen
END IF
LOOP 'loop forever
CALL CloseCom 'close the port and end
Here, the OpenCom routine is called specifying communications
port 1 at a baud rate of 2400, no parity, 8 data bits, and 1 stop
bit. RB512 specifies a receive buffer size of 512 bytes, and XON
indicates that XON/XOFF handshaking is to be performed automat-
ically. We will discuss handshaking as well as selecting an
appropriate buffer size in a moment.
Once the communications port has been opened, an "endless" loop
is entered that alternately checks for keyboard activity and
characters being received. If a character has been entered at the
keyboard it is sent through the port (unless it was the Escape
key). And if any characters have been received and are waiting to
The QBNews Page 24
Volume 2, Number 2 May 31, 1991
be read, the ComInput$ function reads all of them from the receive
buffer.
The ComEof% function is used to determine if any characters are
waiting in the buffer. ComLoc% reports how many, and ComInput$
does the actual reading. Notice that these functions are identical
to the equivalent BASIC functions EOF(), LOC(), and INPUT$
respectively.
The BIOSInkey function is much more efficient than the regular
QuickBASIC INKEY$, because it returns the integer ASCII value
of the key that was pressed. This requires less code in the BASIC
program, since integer assignments and comparisons are simpler
than the equivalent string operations.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
As you can see, using PDQComm isn't too much different from
standard QB or BPDS. Of course, this simple terminal program doesn't
show off all the features of the library. You can set the amount of
time to wait to transmit using ComPrint$. If you'd rather use RTS/CTS
hardware handshaking instead of software XON/XOFF, it's available.
If you want to run another program after closing the comm port but
not drop the line, a routine called SetMCRExit allows you to select
what happens to the state of the DTR and RTS lines when the port
closes. PDQComm has low-level routines to handle just about any task
you can imagine with the serial ports of your PC.
What about high-level stuff? Crescent packages are famous for
throwing in a number of BASIC routines that not only illustrate the
usage of the low-level routines, but provide you with easy to use
building blocks for your own programs. Several terminal programs are
provided including one that runs as a TSR. For those of you who need
to transfer files, there are routines for transferring an ASCII file
(somewhat useless, in my opinion) and a set of XMODEM routines for
both checksum and CRC (far more useful).
If you need to talk to a terminal, terminal emulation routines are
provided for TTY, ANSI (PC), VT52 & VT100 (Digital Equipment), and
D215 (Data General). Routines are provided to define multiple
windows on the screen - each running a different emulation if you
wish.
If you are looking for source code to a basic, high performance
term program that you can customize to your heart's content, PDQComm
furnishes you with enough tools and toys to keep you busy for
months. The AnsiTerm program included works rather nicely as an
example of what you can do with the package; it shows off both ANSI
emulation and XMODEM file transfers. It also demonstrates the use of
the XStat routine, a pop-up status window for tracking XMODEM
transfers.
Technical Support is something that Crescent does well. While
digging through this package, I ran into a problem running one of
The QBNews Page 25
Volume 2, Number 2 May 31, 1991
the demo programs (DemoAnsi). A call to Crescent solved the problem
with a minimum of fuss. If I'd really read the documentation instead
of skimming it, I'd have found the problem. Instead of reminding me
what a dummy I was for not fully reading the manual, the person on
the other end of the phone (Nash) kindly led me through uncommenting
some lines of code to make things work. Microsoft could learn a few
things about customer service from these people. (I wish they
would!)
Nothing in this world (especially in programming) is perfect, but
PDQComm has few flaws. For those planning to write their own BBS, it
would be nice to see more file transfer routines though it can be
easily argued that external programs (like DSZ) can be used to
provide that service. Other than that, and my earlier nit-picking
about the binding, there's not much to complain about.
To sum up, PDQComm is an excellent communications package for BASIC
programmers. It has a well written manual, plenty of examples that
will interest the novice, and plenty of routines to satisfy the more
advanced programmer. Tech support is very good and the routines work
as advertised. The package continues to be improved and Crescent has
already announced that they will be selling a version of PDQComm for
the new Visual Basic. Overall, if you are looking for a competent
communications package, this is money well spent.
**********************************************************************
Jim Harre is an avid QuickBASIC programmer from St. Louis. He also
runs a BBS that has been in operation since it ran on a TSR-80. You
can call his BBS at 314-843-0001 or write to him in care of this
newsletter.
**********************************************************************
The QBNews Page 26
Volume 2, Number 2 May 31, 1991
----------------------------------------------------------------------
W h o y a g o n n a c a l l ? C A L L I N T E R R U P T
----------------------------------------------------------------------
Working with the Rodent by Daniel R. Berry
The Rodent (mouse) is one of the easiest input devices for computer
users to adapt to. It allows great flexibility for entering data,
moving around screens and using menus. Over the last few years the
Rodent has truly found a home with computer users. This leads you to
wonder why new programs enter the market without mouse interfaces.
Probably the most common reason for this oversight is the Rodent
intimidates programmers. The pesky little critter isn't the easiest
user interface to program for. Yet, adding a mouse interface to your
programs is easier than most think.
The mouse functions operate through a device driver such as
MOUSE.SYS or MOUSE.COM provided with most MS-DOS compatible rodent
species. This driver ties itself to interrupt 33H allowing the
programmer direct accessed to the mouse functions. Several programmer
references document this interrupt and its functions detailing the
features and complexities of the mouse driver. Over the last few
years I have played around with mouse interfaces and have finally
developed a set of routines that allow me to add a mouse interface to
any QuickBASIC program I write. The routines provide access to most
of the mouse functions and they aren't that hard to use.
To use these routines in your program you must use the following
statement at the beginning of your program:
COMMON SHARED /Mouse/ MouseChk%, MouseStat%
(This statement is provided in the MOUSE.BI file.)
The routines use these variables to track the following:
MouseChk% indicates the availability of an active mouse driver
and the status of these routines (1=Enabled / 0=Disabled).
MouseStat% shows the status of the mouse cursor or pointer
(1=On / 0=Off).
Mouse is the heart of the mouse utilities. It provides access to
interrupt 33h using INTERRUPT provided in the QB.QLB. Almost all the
other mouse routines call Mouse to complete their functions.
Usage: CALL Mouse (M1%, M2%, M3%, M4%)
M1 - M4 are the system registers AX - DX respectively.
MouseCheck is the second most important of these routines.
MouseCheck can determine mouse driver availability; enable the mouse
driver; or disable the mouse driver. This routine must be called at
the beginning of your program if you want to use the mouse.
The QBNews Page 27
Volume 2, Number 2 May 31, 1991
MouseChk updates MouseChk% and MouseStat% as applicable. You
should note that when the mouse driver is installed the mouse cursor
is set to off.
Usage: CALL MouseCheck (MFlag%)
MFlag% = If set to -1 the mouse will be installed, if
available (if not already done so). If set to -2 the mouse
routines will be disabled. Any other value will cause this routine
to determine if the mouse has been installed. A returned value of
1 indicates that the mouse driver is installed, 0 indicates that
the mouse driver has not been installed or is disabled.
MouseCRT will determine or set the mouse video display page. This
is useful when your programs use multiple display pages.
Usage: CALL MouseCRT(CRT%, Flag%)
The video display page is determined or set by using the CRT%
variable. Flag% is used to toggle 0=set/1=determine.
MouseInformation provides specific information for the Rodent used
by the system. This routine provides the mouse driver software
version and mouse type.
Usage: Call MouseInformation(MouseVer$, MouseType%,
MouseType$)
MouseVer$ is the current mouse driver version.
MouseType% and MouseType$ represent the following:
1 - Bus Mouse 2 - Serial Mouse 3 - InPort Mouse
4 - PS/2 Mouse 5 - HP Mouse
MouseLeft determines the position of the mouse at the last left
button depression. Also determines the number of depressions of the
left button since this routine was last called. Position is returned
in screen pixels.
Usage: CALL MouseLeft (LCount%, MouseX%, MouseY%)
LCount% = The number of left button depressions since the
routine was last called.
MouseLeftRC functions the same as MouseLeft except position is
determined by screen row and column.
Usage: CALL MouseLeftRC (LCount%, MRow%, MCol%)
MouseLightPen will enable (1) or disable (0) mouse light pen
emulation mode. Calls to the Basic PEN function return the last pen
down location. Pen down is set when both buttons are depressed.
The QBNews Page 28
Volume 2, Number 2 May 31, 1991
Usage: CALL MouseLightPen (Status%)
MouseLimits will set the mouse limits restricting movement to a
specific area. Limits are set using screen pixels.
Usage: CALL MouseLimits(MouseX1%, MouseY1%, MouseX2%,
MouseY2%)
MouseX1/Y1% = Upper left corner horizontal/vertical (X/Y)
position.
MouseX2/Y2% = Lower right corner horizontal/vertical (X/Y)
position.
MouseLimitsRC functions the same as MouseLimits except the limits
are determined by screen row and column.
Usage: CALL MouseLimitsRC(MRow1%, MCol1%, MRow2%, MCol2%)
MRow1%/MCol1% = Upper left corner row/column limit.
MRow2%/MCol2% = Lower right corner row/column limit.
MouseLocate locates the mouse cursor at a specified X and Y
coordinate. Coordinates are set in screen pixels.
Usage: CALL MouseLocate(MouseX%, MouseY%)
MouseLocateRC functions the same as MouseLocate except coordinates
are set at specified row and column.
Usage: CALL MouseLocateRC(MRow%, MCol%)
MouseMotion returns the horizontal and vertical mouse motion
counters. Motion counts are in 1/200 inch increments.
Usage: CALL MouseMotion(Horz%, Vert%)
Horz% = Horizontal motion counter value (positive = right
motion / negative = left motion).
Vert% = Vertical motion counter value (positive = downward
motion / negative = upward motion).
MouseOn / MouseOFF turns the mouse cursor on/off as desired. These
functions use MouseStat% to determine the status of the cursor and
take action only when needed. This check is used to prevent the
internal cursor flag from being modified unnecessarily.
Usage: CALL MouseOn / MouseOff
MouseRelease returns the position of the mouse after the button
requested was last released. Position is returned in screen pixels.
The QBNews Page 29
Volume 2, Number 2 May 31, 1991
Usage: CALL MouseRelease(Button%, Count%, MouseX%, MouseY%)
Button% = The button to be checked (0 = left / 1=right).
Count% = The number of button releases since the last call to
this routine.
MouseReset resets the mouse driver to the default values and
updates MouseStat%.
Mouse Position: Screen Center Mouse Cursor: Off
Light Pen Emulation: On CRT Page Number: 0
Usage: CALL MouseReset
MouseRight determines the position of the mouse at the last right
button depression. Also determines the number of depression of the
right button since this routine was last called. Position is returned
in screen pixels.
Usage: CALL MouseRight(RCount%, MouseX%, MouseY%)
MouseRightRC functions the same as MouseRight except position is
determined by screen row and column.
Usage: CALL MouseRightRC (RCount%, Row%, Col%)
MouseSensitivity enables programmers to determine or modify mouse
sensitivity to better suit their needs. Horizontal and vertical
movement are defined in mickeys. One mickey equals 1/200-inch of
mouse travel. Mickeys range from 1 to 32,767 (horizontal default is 8
and vertical default is 16). Doublespeed sets threshold for doubling
mouse cursor movement (default 64 mickeys/second).
Usage: CALL MouseSensitivity(GetSet%, HMickey%, VMickey%,
DoubleSpeed%)
GetSet% is used to toggle 0 = Get / 1 = Set.
MouseStatus determines Mouse Status to include position and buttons
currently depressed. Position is returned in screen pixels.
Usage: CALL MouseStatus(Left%, Right%, MouseX%, MouseY%)
Left% = If returned as 1 then the left button is depressed.
Right% = If returned as 1 then the right button is depressed.
MouseStatusRC functions the same as MouseStatus except position is
returned by screen row and column.
Usage: CALL MouseStatusRC(Left%, Right%, MRow%, MCol%)
RODENT.BAS provides a demonstration of these routines and designed
The QBNews Page 30
Volume 2, Number 2 May 31, 1991
to give you a general idea of how the mouse interface works. If your
rodent is having a problem working with these routines read your users
manual to ensure that you have MS Mouse emulation active.
Well, that's about it. I have included a few support routines from
my library to give RODENT.BAS some flair and the *.BIT routines are
needed by MOUSE.MOU to work with some of the bits returned in the
system registers. You have everything you need to get that pesky
rodent to work so why not put that $50 - $90 toy to some use and
program!
Please feel free to write. I am open to comments, suggestions and
will even answer a question or two. Complaints are received only on
Friday, must be in triplicate (no carbons please), must be signed by
your mother, and be accompanied by a personal check for $200.00.
(Laugh - my wife did!)
[EDITOR'S NOTE]
All source code for this article is contained in RODENT.ZIP.
**********************************************************************
Daniel Berry is a member of the U.S. Air Force. Off-duty he
writes a utility library for QuickBASIC. Daniel may can be reached
at 3110-C South General McMullen, San Antonio, TX 78226.
**********************************************************************
The QBNews Page 31
Volume 2, Number 2 May 31, 1991
A Graphics Mouse Cursor Design System by Dr. Warren G. Lieuallen
MOUSCURS.BAS will allow you to draw your own graphics cursor
design without having to worry about all those crazy hex numbers,
and what to do with them! You'll simply draw your cursor shape
on the screen, and then save the data. A complete, stand-alone
QuickBASIC program will be created which will simply define your
cursor and turn it on. The purpose of this is for you to then
add the rest of your program; MOUSCURS will have taken care of
the nitty-gritty of your custom mouse cursor. For the more
adventurous of you, you can also save only the DATA statements,
and then do with them what you will.
MOUSCURS.BAS was designed and tested on a Hercules
monochrome system, and checked on a VGA system. It should work
with any graphics system, but I've not used it with CGA or EGA.
If you have trouble, feel free to fiddle with the code yourself.
To use MOUSCURS.BAS, you must load QuickBASIC with CALL
Interrupt support. The easiest way to do this is to load the
QB.QLB quick library by entering "QB /L QB" to run QuickBASIC.
You'll also need CALL Interrupt support for your program which
uses MOUSCURS to define the cursor. My recommendation is to add
the CALL Interrupt routine to your own custom quick library;
that's what I did!
Once you run MOUSCURS, you'll seen the main (and only!)
screen. You can now use your mouse to draw a shape. Move around
the "cursor mask" screen; pressing the LEFT button will toggle
the individual pixels on and off. Once your shape is defined
(you'll see a true-size representation of the cursor in the
"Custom Cursor" window), you'll then need to work on the screen
mask. The easiest way to do this is to click on the "Expand".
This will copy your cursor to the screen mask window, and draw a
border around it automatically. You can then edit it to
perfection; watch the "Custom Cursor" window to see what the
cursor looks like on a "normal" and "inverse" screen.
Before you finish, you must also define a "hot spot". This
is the single pixel which defines the cursor's location (e.g. the
spot that must be inside the button you're clicking on). To
select a "hot spot" click the RIGHT button in the cursor mask
window. The hot spot will be indicated by an "X". However, by
clicking in the same spot, you can define an "invisible" hot
spot, which will be defined by a "0". The best way to learn how
the RIGHT button works is to just play with it, and watch the
"Custom Cursor" window to see if the spot is turned on or off.
Also, remember to use the RIGHT button for the hot spot, and the
LEFT button for everything else.
At any time, you can also clear either of the windows by
clicking on the "Clear" buttons. You can also copy the cursor
mask to the screen mask by clicking on the "Copy" button.
The QBNews Page 32
Volume 2, Number 2 May 31, 1991
Finally, as a test of your new cursor, you can click on the
"Activate" button to replace the cursor with your own design. Be
sure you have something defined before you do this, or you'll
lose the cursor, and won't be able to see what you're doing! You
can turn your cursor back off and return to the hand by clicking
on the "DeActivate" button.
Once your cursor is finished, you can save it by clicking on
either the "Save Data" or "Save Program" buttons. "Save Data"
does just what it says; it will save only the DATA statements
needed to define your custom cursor. "Save Program" will create
a stand-alone program that will define your cursor and turn it on
(in fact, you can run this program just to see what your cursor
looks like once you're done). Both of these options create a
file called CURSORn, with "n" being a sequential number; you will
not overwrite existing cursor files (old files must be deleted
manually). Data is saved with a .BI extension; programs are
saved with the standard .BAS extension. You can, of course,
rename any of these files after you've created them via DOS.
Finally, if you've already created some cursors, you can
edit them by using the "Load Cursor" button. You'll be asked to
provide the filename and extension; the data will then be loaded
and the cursor displayed just as you had drawn it.
Feel free to examine the code of MOUSCURS.BAS. It was
written primarily as an learning exercise; there are undoubtedly
other (and probably better!) ways to accomplish the same task.
MOUSCURS.BAS was my first (and so far, only) graphic program, so
please bear with me. For more details on programming mouse
functions in QuickBASIC, refer to the QB News (volume 2,
number 2).
[EDITOR'S NOTE]
The code for this article can be found in MOUSECUR.ZIP. This program
will not work in the QBX environment or with far strings. This
program also requires the DIR$ function contained in the SwapShop
section of this newsletter.
The QBNews Page 33
----------------------------------------------------------------------
T h e Q B N e w s P r o f e s s i o n a l L i b r a r y
----------------------------------------------------------------------
Fast Screen Printing by Christy Gemmel
[EDITOR'S NOTE]
All code for this article may be found in FASTPRNT.ZIP. This article
also refers to figures which included in FIGURES.TXT which is in
FASTPRNT.ZIP.
This article presents a rapid method of displaying a string of ASCII
characters on the screen without updating the cursor position. The
calling program should pass the string, the row/column co-ordinates of
the screen location where it is to be printed and the number of the
colour or display attribute required.
Many routines in my Assembly-Language Toolbox are concerned with the
video display, this being the primary means through which we
communicate with the users of our program. Later, when we get around
to developing such things as Popup Windows and Dialogue Boxes, we will
need to acquire some pretty sophisticated screen-handling techniques.
For now, however, let us look at the more mundane task of displaying
text on the screen.
For most applications the QuickBASIC PRINT statement is perfectly
adequate. Because it is a general-purpose statement, however, which
can be used to direct output to the printer or a file as well as to
the screen, PRINT is not as fast as it might be. Moreover, to display
a string of characters at a specific place on the screen you must
first position the cursor with a LOCATE statement. Then, if you want
to display the string in a different colour than the background text,
you must issue a preliminary COLOR statement before printing. What we
need is a screen-specific display routine that combines these three
statements into one. Naturally, since we are going to the trouble of
writing it in Assembly-Language, we expect it to be as fast as
possible.
Let us, then, set out our objectives for FastPrint:
1) The ROW/COLUMN location where the string is to be printed is
specified to the routine, so that there is no need for a seperate
LOCATE statement to position the cursor.
2) You must be able to place the string anywhere on the display,
including the bottom line of the screen, no matter what VIEW
PRINT parameters may be in effect.
3) Output to the bottom line of the display should not cause the
screen to scroll.
4) You should be able to specify the colour and/or display attribute
of the text to be printed, so there is no need for a seperate
COLOR statement. Furthermore the attribute specified should appy
The QBNews Page 34
Volume 2, Number 2 May 31, 1991
only to the text being printed, it must not effect the colour of
any subsequent PRINT statements.
5) To obtain maximum performance, FastPrint should write directly to
video display memory. This will be particularly useful when we
need to output large blocks of text.
This last clause needs some explanation. In general, there are two
methods of outputting text to the screen. The first makes calls to the
video services built into the PC's ROM-BIOS and can, consequently, be
used by any computer that is BIOS-compatible with the IBM family of
personal computers. This method, however, is considered to be a little
slow by today's standards.
The alternative technique bypasses ROM-BIOS completely and writes
directly to the video display buffer. Since the display, in most PCs,
is mapped by the video hardware to a fixed location in memory, any
characters that are written to this area will immediately appear on
the screen. This is the fastest method of all, but has the
disadvantage that it will only work on computers that are hardware-
compatible with the original IBM PC. However, since this includes all
modern XT, AT and PS/2 class machines, it is not unduly restrictive,
but it does eliminate some older models like the Tandy 2000.
We will adopt the second method. It is, after all, the one used by 90%
of today's software and has become something of a de facto standard in
its' own right. This is the only way we can achieve some real
performance in our programs.
The bulk of the work is done by a short assembly-language procedure
that is designed to be called from QuickBASIC. It can be part of a
Quick Library for use in the programming environment or assembled into
a single module that can be linked to stand-alone programs compiled
with BC's /O switch. I will show you both methods later.
Although short, FASTPRINT illustrates some important features of
mixed-language programming that I will discuss in some detail. It was
written for MicroSoft's Macro Assembler (MASM) version 5.1 and makes
use of the simplified segment directives that are a feature of that
assembler. This lets us define the Memory Model of the routine to
match that of the calling program.
.model medium
All QuickBASIC programs use the MEDIUM memory model, which means that
all simple data fits within a single 64K segment, although the program
code may be greater than 64K. When we enter the routine, therefore,
the DS (Data Segment) register is already pointing to the segment
containing the string of characters we will be printing and we can use
near pointers, offset from DS, to address this string. The main
procedure, however, must be declared FAR so that the Code Segment (CS)
register is set correctly when it is called. FastPrint is the name you
will be using when you call this routine from your program, declare it
PUBLIC so that QuickBASIC can find it.
The QBNews Page 35
Volume 2, Number 2 May 31, 1991
public FastPrint
Now for the routine itself, here are the first few lines:
.code
FastPrint proc far
push bp ; Save Base pointer
mov bp,sp ; Establish stack frame
push es ; Save Extra Segment
push si ; and index pointers
push di
Remember that QuickBASIC uses the Medium Memory model. This means that
we can't assume our assembled routine will be linked into the same
code segment as the main program that calls it, so the procedure must
be declared as FAR. The next two lines are concerned with setting up a
pointer to the stack so that we can easily get at the parameters that
the routine is expecting, more about this in a minute. We must also
preserve the values of any segment registers (except for CS, which has
already been modified by the call itself) that are altered by the
routine, also the Index registers DI and SI if they are used.
When the routine is called, QuickBASIC pushes a two-byte value
corresponding to each of the supplied parameters onto the program
stack. These are followed by the return address which, since this is a
FAR procedure, is a four-byte pointer (Segment and Offset) to the
statement following the call to FastPrint. When we have finished
saving our own working registers, the stack will look like this:
Figure 1.1 Stack contents after entry into FastPrint.
Note that, since we copied the contents of the Stack Pointer to BP
immediately after BP itself was pushed, This register can now be used
as a fixed pointer through which we access the parameters that were
passed. Normally, QuickBASIC passes these arguments as 2-byte offset
addresses, which point to the named variables in the program's Data
Segment.
We, however, are going to override this and pass the numeric arguments
BY VALue, forcing QuickBASIC to copy the actual contents of the
variables into the appropriate stack locations. Then all we need do is
read the data supplied, directly into the target registers:
mov ax,[bp+12] ; Get row number
Passing by VALUE saves us having to hunt for the variables containing
our data and so makes our routine faster and more compact. It also,
incidently, makes it easier if we ever need to convert FastPrint to a
C function, since the C language does it by default. We'll make use of
the technique in a minute or two, but first we must collect some
important information about the computer our program is running in.
The QBNews Page 36
Volume 2, Number 2 May 31, 1991
Although we have decided that our program need only support machines
that are hardware-compatible with the IBM PC family, this still leaves
a variety of video environments to cater for. We must, for example,
find out whether the host computer is fitted with a colour or
monochrome display adaptor since the address of the screen buffer we
will be writing to depends on this. Computers with EGA and VGA cards,
moreover, can switch between up to eight display pages so, if our
program is running in one of these machines, we must make sure that
FastPrint sends its output to the correct page. Last, but not least,
if the host system does have an EGA or VGA we must find out whether
the current screen is set to a height of 25, 43 or 50 rows, so that we
can format our printing properly.
We could, of course, code the instructions that collect this data
directly into FastPrint itself. It is likely, however, that many
other programs we write will need the same information. Better, then,
to make our video system identification function an independent
procedure that can be called by whichever program needs it. This saves
us repeating the same block of code in every screen routine and makes
the Toolbox tighter and more efficient.
call VideoType ; Get video parameters
Since VideoType, as we have just decided, is going to be called by
many Toolbox routines, we must make sure that the linker can find it
when your program is built. This means that we have to declare it
PUBLIC by putting the following line at the top of the source code.
public VideoType
Note that versions 5 and 5.1 of MASM have a bug which makes all
procedures PUBLIC by default. If you are using either of these
versions of the Macro Assembler, therefore, this line is not strictly
necessary. However, because we cannot rely on this feature being
retained in future releases, it is best to use an explicit PUBLIC
declaration.
VideoType also needs some space to store the data it collects. Insert
the following lines in your source file, immediately after the .CODE
segment declaration but before the first PROC statement. I will
explain them more fully in a few moments.
SnowFlag db 0 ; Snow prevention flag
VideoRam dw 0B000h ; Default video segment
VideoPort dw 03BAh ; Default video status port
Param1 label word
Mode db 7 ; Current screen mode
Columns db 80 ; Current screen width
Param2 label word
Rows db 25 ; Current screen height
ActivePage db 0 ; Current video page
Notice that VideoType, like FastPrint itself, is a FAR procedure. This
is necessary if it is to be called by routines in seperately-linked
The QBNews Page 37
Volume 2, Number 2 May 31, 1991
modules that may have different Code segments.
See the VideoType routine in DISPLAY.ASM.
VIDEOTYPE
As with all assembly-language procedures, it is good practice to
preserve the contents of any registers that will be changed by the
routine and are not used to return data. Do this by pushing the
contents of these registers onto the stack immediately after entry and
popping them again just before you return.
Our first task is to check the current display mode to see if we are
running in a machine with a colour or monochrome monitor. The easiest
way is to call a service routine in the computer's BIOS and let the
system do the work for us. Fortunately there are many such services,
grouped by the hardware devices that they interface with, and with
each group being accessed through a dedicated software interrupt. The
Video services are called through Interrupt 16 (10 Hex).
To gather the information we want, VideoType issues an INT instruction
together with the interrupt number. Interrupt 16 (10 Hex) is a gateway
to some very useful video services. When called with AH set to 15 (0F
hex) it returns with the current display mode in AL, the number of
screen columns in AH, and the current video page in BH.
An interrupt is a signal to the microprocessor that its attention is
required. When one occurs, the processor suspends the current task and
jumps to a routine that is designed to deal with the event that
triggered off the interrupt, this routine is called an interrupt
handler. When the handler has done whatever is required, control
returns to the next instruction in the interrupted task and execution
resumes from there. Just like the return from a BASIC subroutine.
The 80x86 family of processors allows for 256 diffent interrupts,
numbered from 0 to 255. Some are dedicated to hardware devices, every
54.936 milliseconds, for example, the 8253 timer chip generates
interrupt number 8, which executes a subroutine to update the system
clock and date and time flags.
Most interrupt numbers, however, are reserved for software interrupts
that can be generated by programs, using either the assembly-language
INT instruction or the QuickBASIC CALL INTERRUPT statement.
You call an interrupt handler, not by specifying its address, but by
supplying the interrupt number you require. The actual addresses are
stored in an interrupt vector table at the very beginning of memory,
being written there by the initialisation process when your computer
is booted-up. When an interrupt is issued, the processor simply looks
up the table entry for that interrupt number and jumps to the address
contained there. Because different models of computer have different
hardware configurations as well as different versions of DOS and BIOS,
the actual addresses in the table will vary from machine to machine.
But, since the interrupt numbers themselves do not change, programs
The QBNews Page 38
Volume 2, Number 2 May 31, 1991
that use them are still portable between all computers in the PC
family.
When an interrupt is executed, the processor makes the following
actions:
1) The FLAGS register, the current Code Segment (CS) and the
Instruction Pointer (IP) are saved on the stack.
2) The address of the handling routine is looked up in the Interrupt
Vector Table, starting at location 0000:0000 in memory. Since
each table entry is four bytes long, the correct entry can be
found by multiplying the interrupt number by four.
3) The segment and offset address of the interrupt handling routine
are loaded into the CS:IP registers, transferring control to that
routine.
4) The code at the handling routine is executed until the
processor encounters an IRET (RETurn from Interrupt) instruction.
5) The original Instruction Pointer, Code Segment and Flags register
are then restored from the stack and execution proceeds with the
instruction that followed the original interrupt call.
Figure 1.2 The interrupt sequence used on computers such as the
IBM-PC, which use the Intel 80x86 family of micro-
processors.
On return from Interrupt 16, VideoType first checks the current video
display mode. Notice that we are looking to see if AL contains a value
of 7. This is only returned if your computer has a monochrome display.
If AL does equal 7, we can skip the rest of the procedure, since our
local variables are set for a monochrome display by default. Anything
other than 7, however, means that we have a graphics adaptor to
contend with and must adjust the defaults accordingly.
Two variables are involved. The first, labelled VIDEORAM is set to the
segment address of the display memory we will be using. This is at Hex
0B000 for mono adaptors (the default) and 0B800 for colour adaptors.
The other variable is the number of the hardware port that controls
the video display. For mono adaptors this is numbered 3BA (hex). For
colour machines the port number must be reset to 3DAh.
Notice that we are accessing local variables through the CS register,
using a SEGMENT OVERIDE directive; e.g.
mov CS:VideoRam,0B800h
The reason for this is that the DS register, which is normally used to
address data, is still pointing to the calling program's Data Segment.
We need to leave DS intact so that we can use it to get at our display
string when we eventually return to FastPrint. For now, however, we
carry on and store the screen width and display mode, which were
The QBNews Page 39
Volume 2, Number 2 May 31, 1991
returned in the upper and lower bytes of AX, in another of our local
variables. The active page, which is in BH, is saved, temporarily, on
the stack until we can obtain the screen height to go with it.
In computers fitted with EGA and VGA adaptors the video hardware adds
its own set of video functions to the BIOS video services. One of
these, interrupt 16 sub-function 17/48, returns amongst other things,
the information we require; the screen height in rows set in DL.
What if our host computer doesn't have an EGA or VGA? No need to
worry, calling an interrupt service that doesn't exist will not cause
an error. All that happens is that the function returns with registers
unchanged. Since other video adaptors only allow screen heights of 25
rows, if we previously set DL with this number, the correct value will
still be in the register after the interrupt call. Even if an EGA or
VGA isn't present. Notice, however, that the value we set is actually
24. BIOS routines typically use a numbering system based on zero
rather than one, so a screen height of 25 rows is returned as 24. This
is why we increment the return value by one, before storing it in its
destination variable.
One final thing that VideoType must do is to detect whether the host
computer is using a Colour Graphics Adaptor (CGA). The reason for this
is that there is a special problem with CGAs when we write directly to
video memory. I'll go into more detail about this later. For now we
just need to set a flag to indicate the presence of a CGA.
How do we find out if this computer has a CGA? Easy. The trick is to
find a video interrupt service which only works on machines fitted
with an EGA or VGA and set an impossible value into one of the return
registers. If, after the interrupt call has completed, this register
is unchanged then an EGA or VGA is not present and we must have a CGA.
Remember we have already eliminated systems with Monochrome Display
Adaptors (MDA). Video service 18/16 is a suitable candidate. It
returns a value of between 0 and 3 in BL to indicate the amount of
memory, in 64K blocks, installed on your video card. VideoType presets
BL with 16 (10 hex) before calling the interrupt. If this value is
still in BL when the interrupt returns, then we have a CGA to contend
with and we must set the SnowFlag accordingly.
Before popping its saved registers and returning, VideoType's final
task is to retrieve all the information it collected from local
storage. Back in FastPrint, we resume our task by isolating the
information that is needed next, the current screen dimensions. These
are collected in DX so that we can check them against the start
position of the string to be printed, making sure that it is within
the screen boundaries.
mov dh,ah ; Load screen dimensions
mov dl,bl ; into DX
QuickBASIC, conventionally numbers screen rows from 1 to 25 and screen
columns from 1 to 80. Since the ROM-BIOS co-ordinate system uses row
and column numbers starting from zero we need to convert our
The QBNews Page 40
Volume 2, Number 2 May 31, 1991
parameters to base zero before we can start printing. At the same
time it would be a good idea to check for legal values.
This is where we start collecting the parameters that were passed to
us by our caller. First the row co-ordinate:
mov ax,[bp+12] ; Get row number
dec al ; Convert to base zero
cmp al,0 ; Top screen row
jae Fast_01 ; Jump if not below
xor al,al ; Don't go over the top!
Fast_01:
cmp al,dl ; Bottom row?
jb Fast_02 ; Go no further
mov al,dl ; Substitute maximum
dec al ; row number
Now let's do the same for the column co-ordinate supplied:
Fast_02:
mov bx,[bp+10] ; Get column number
mov ah,bl ; into AH
dec ah ; Convert to base zero
cmp ah,0 ; Leftmost column?
jae Fast_03 ; Jump if not below
xor ah,ah ; Don't fall off the screen
Fast_03:
cmp ah,dh ; Rightmost column?
jb Fast_04 ; Go no further
mov ah,dh ; Substitute maximum
dec ah ; column number
Once we have a set of legal row and column co-ordinates we need to
translate them into the actual address in the video display buffer
where the data will be written. Since, like VideoType, this is a task
that our Toolbox routines will have to perform often, we had better
write a seperate procedure to handle it.
Fast_04:
call ScreenAddress ; Calculate target address
ScreenAddress, again, must be a FAR procedure so that it can be called
from programs in seperately-linked modules that may have different
Code segments. As always, we preserve on the stack any working
registers that are not used to return values. The row and column co-
ordinates which ScreenAddress will convert into a memory address are
supplied in the low and high bytes of AX.
See the ScreenAddress routine in DISPLAY.ASM.
ADDRESSING MEMORY
The 80x86 CPU uses two registers to fully describe memory addresses.
It does this by dividing the absolute address by 16 and storing the
The QBNews Page 41
Volume 2, Number 2 May 31, 1991
result of this calculation in a SEGMENT register. Then another, INDEX,
register is used to hold the remainder, which is called the OFFSET of
the object from the start of the segment. The byte at absolute decimal
location 753666, for instance, would be addressed as;
Segment = (753666 \ 16) = 47104 Offset = (753666 MOD 16) = 2
Using hexadecimal (base 16) notation, makes it much simpler to work
with segments and offsets, since all you need do is shift all digits
of the absolute address, one place to the right, the rightmost digit
falling off to become the offset part of the address:
753666 = B8002 (Hex) --------> Segment B800 : Offset 0002
More conventionally, this is written by assembly-language programmers
as B800:0002. It is actually the memory address of the character
displayed in the second column of the top row of the screen (if your
computer has a color adaptor).
Using this method, the PC's 1-megabyte of address space can be divided
into 65536 (0 - FFFFh) possible segments. Each segment starts at a
different paragraph boundary (a paragraph is a block of 16 bytes) and
contains up to 65536 (0 - FFFFh) different byte addresses. Any segment
larger than 16 bytes, of course, will overlap with the segments
starting at the next and subsequent paragraph boundaries.
To obtain the address of the position on the screen where our text is
to be printed, ScreenAddress's first move is to copy the supplied
column co-ordinate into BH. It then obtains the screen width, in
columns, from the local variable that was previously set by VideoType.
Since each column on the screen takes up two bytes of memory, one for
the character stored there and the other for the attribute that
governs how it is displayed, we now need to multiply the screen width
by two to get the number of bytes per row. In assembly-language this
is easy; the SHL instruction shifts all bits of the binary number in
BL one position to the left, effectively doubling its value.
Since AL contains the row number, we can now multiply it by BL and
get the byte offset of this row. The MUL instruction extends the
result of this calculation into AX, which is why we moved the column
number into BH out of the way. Now we must use SHL again to convert
this into bytes and add the result to AX. We now have the target
address, offset from the beginning of the screen and it only remains
for us to copy it into DI, one of the index registers used for
addressing memory.
If PCs only used a single screen page we could return right away.
However machines with CGA, EGA and VGA cards are capable of displaying
multiple pages so we must find which one is currently on the screen.
We already have the page number from VideoType but BIOS makes it even
easier by providing the actual address of the current display page,
which is what we really want.
The QBNews Page 42
Volume 2, Number 2 May 31, 1991
We don't need to use an interrupt call this time. BIOS maintains its
own data block at segment 40h in low memory and this contains, amongst
many other things, the exact information that we're looking for. The
address of the current display page is stored as a two-byte number at
offset 4Eh into this block. Notice, however, that ScreenAddress
actually uses 0000:044Eh to reach it. This is because it is slightly
quicker to clear the ES segment register than it is to load it with a
value. The actual memory location remains the same.
Figure 1.3 PC Memory map highlighting the BIOS data area in low RAM
Once we have the page offset all that remains is to add it to the row
and column offset that is already in DI. VideoType has already stored
the video segment and port address for us so we just need to retrieve
these parameters before returning to FastPrint.
The result of all this register juggling is that we now have the ES:DI
register pair pointing to the address in memory where output is to
start. DX is also loaded with the address of the video controller
port. Now we're ready to find the string to display.
What QuickBASIC passes when a string is specified as parameter to an
external procedure, is a pointer to a STRING DESCRIPTOR in the Data
Segment. This is a four-byte block of data of which the first two
bytes are the string length. In QuickBASIC strings may be up to 32767
bytes in length:
Figure 1.4 Structure of the String Descriptor passed by QuickBASIC
The second two bytes of the string descriptor contain a pointer to the
first character of the string itself. This is an offset address,
relative to the start of DGROUP, QuickBASIC's default Data Segment.
This is the address that is returned by the SADD function in
QuickBASIC. When used with a string, VARPTR returns the address of the
string descriptor instead.
If you are using Microsoft's BASIC 7 Professional Development System
then the method of passing strings is rather different. See the
sidebar for more details.
Before going any further, we should check that there is a string to be
printed. If the first two-bytes of the string descriptor evaluate to
zero, a null string has been passed so there is no point in proceeding
further. Consequently we set the AX register to an error code and jump
to the exit. It is good practice to return a code indicating the
success or failure of an operation when we leave, even if the calling
program will not read it. Most DOS applications return such an
ERRORLEVEL as a matter of course.
Well the string is there, so we can load its address, which is the
second two bytes of the string descriptor, into our other Index
Register, SI.
The QBNews Page 43
Volume 2, Number 2 May 31, 1991
mov bx,[bp+8] ; Index to string descriptor
mov cx,[bx] ; Get string length
cmp cx,0 ; Make sure it isn't
ja Fast_05 ; a null string
mov ax,1 ; If so set Error code
jmp short Fast_07 ; and abort
Fast_05:
mov si,[bx+2] ; DS:SI==> string data
mov ax,[bp+6] ; Get display attribute
xchg ah,al ; into AH
cld ; Clear direction flag
This is a good opportunity to get the final parameter, the display
attribute we are to use, into the AH register. The CLD (Clear
Direction Flag) instruction ensures that any subsequent string
manipulation instructions move data forwards in memory. Now that we
are ready to start printing, the processor registers are set up as
follows:
Segment Registers: ES points to the current video segment.
DS points to the calling program's DATA segment.
Index Registers: DI points to the offset address, relative to ES,
where the string will be copied to.
SI points to the beginning of our source string,
relative to the DS register.
General Registers: DX contains the video status port address.
CX contains the length of the source string.
AH contains the display attribute to be given to
each output character.
The last section of code is a simple loop that copies each byte of the
source string to its destination address in video memory. Instead of
using a MOV instruction to copy each byte of the string into the AL
register before transferring it to the screen, we use another assembly
instruction, LODSB.
Fast_06:
lodsb ; Get a byte from the string
LODSB is one of a set of string manipulation primatives built into the
Intel family of microprocessors. Why they are called 'primitive' I
don't know. They are, in fact, extremely powerful. In just one
statement, LOSB will load a byte from the address pointed to by DS:SI
into the AL register and then automatically increment SI to point to
the next byte. If we were to prefix it with a REP instruction it would
repeat the whole operation for the number of times that the value in
CX is set to. This, as you can see, is just the thing for loops.
In fact, if we didn't have to cater for systems with CGA video cards,
we could do the remainder of the job in just one line. LODSB has a
companion instruction, MOVSB, which, as it's name implies, moves the
byte at DS:SI to a destination address pointed to by ES:SI. In this
The QBNews Page 44
Volume 2, Number 2 May 31, 1991
case both of the index registers, SI and DI, are incremented ready for
the next byte to be transfered. Like LODSB, MOVSB takes a REP prefix
and, since we already have the length of our string in CX, all that
would be necessary is to enter the instruction ....
rep movsb ; Shift the whole string
... and the job would be done.
But we do have to contend with CGA cards so our task is a little more
complicated and, since it is one that we will need to repeat in other
programs, we had better do it in a sub-routine.
call ScreenWrite ; Write a byte and attribute
loop Fast_06 ; Repeat for each character
See the ScreenWrite routine in DISPLAY.ASM.
The problem with CGAs occurs only when we write directly to video
memory. If the 6845 CRT Controller attempts to fetch a character from
the screen buffer whilst data is being written to that same location,
the resulting interference will cause 'snow' or glitches to appear on
the screen. We can prevent this happening by making sure that we only
output to the display when the 6845 is not reading from it.
Fortunately we do not have to wait long, since every 12 microseconds,
the 6845 performs what is known as a horizontal retrace. During this,
time the controller is not doing any reading so it is safe for our
program to write to the display.
We can detect when a horizontal retrace is in progress by testing bit
zero of the video adaptor status port, its address is now in DX,
remember? If this bit is set then a retrace is under way. In practice
we let any current retrace complete and wait for the next to begin,
before moving data. This ensures that we have we a full retrace period
in which to place the character and attribute byte into their proper
buffer addresses.
Computers with monochrome adaptors and those with more modern versions
of the CGA, do not experience 'snow' problems and, if you are sure
that you will only be using FastPrint in such systems, you are welcome
to leave out the routine. Me, I wouldn't bother. Using it ensures a
good clean display, no matter which computer you use the program in,
and the increase in processing time does not slow your programs down
enough to be noticed. It is a lot faster than PRINT, at any rate.
All that remains, now that our string has been output to the display,
is to tidy up the stack and return safely to the QuickBASIC program
that called us.
First we recover all the registers that were pushed on entry to the
routine.
xor ax,ax ; Report success
The QBNews Page 45
Volume 2, Number 2 May 31, 1991
Fast_07:
pop di ; Clean up the stack
pop si
pop es
pop bp
The final return instruction must adjust the stack so that any
parameters that were originally passed are now discarded. Our routine
received four parameters, each of which took up two bytes of stack, so
we issue a RET 8 to decrement the Stack Pointer by eight, ensuring
that the correct return address is popped back into CS:IP.
ret 8 ; Return to Quick BASIC
FastPrint endp
end
Last of all, don't forget to tell the assembler where the current
procedure and program end.
Here is the full listing of FastPrint again. If you type it in, don't
forget to include the code for the three procedures ScreenAddress,
ScreenWrite and VideoType in their proper places before the final END
directive. Notice that, since all the routines in the file are related
to the video display, I have given it the name DISPLAY.ASM. I suggest
that you use this name as well. We are going to add to it in the
ensuing chapters.
ASSEMBLY AND LINKING
Since all the procedures we have written so far are concerned with the
video display, I propose that we call the source file that contains
them DISPLAY.ASM. Save it as such and then assemble it using the
following command:
MASM /V/W2/Z display;
The /V (VERBOSE) switch instructs the assembler to report statistics
such as the number of program lines and symbols processed.
The /W2 switch sets the assembler's WARNING level to maximum. This
instructs it to report statements that may result in inefficient code,
as well as actual errors. It reports, for example, when you have
issued a normal JMP instruction where a short jump will do. This helps
you write tighter code.
The /Z switch directs MASM to list any lines, where errors are found,
on the screen. Another help in debugging.
If you use an assembler other than MASM, of course, the above
instructions do not apply. In this case consult your own assembler's
documentation.
If the assembly is successful, you will have a file, DISPLAY.OBJ,
The QBNews Page 46
Volume 2, Number 2 May 31, 1991
containing the assembled object code. This is all ready for linking
directly to compiled Quick BASIC programs which are to be run from the
DOS command line. You can link it with the instruction:
LINK yourprog DISPLAY;
(where 'yourprog' is the name of your own program)
If you want to use FastPrint in the QuickBASIC environment, you will
also have to include it in a Quick Library. Use the Linker for this as
well, the command is:
LINK /QU DISPLAY,,,BQLB45.LIB;
BQLB45.LIB is the standard library interface module supplied with
QuickBASIC 4.5, (if you are using the Extended QuickBASIC which comes
with MicroSoft's BASIC 7 PDS, the equivalent module is named
QBXQLB.LIB). Whichever one you use, make sure that it is in the
current directory when you issue the LINK command. You should now have
a file DISPLAY.QLB.
If you've got this far, you will, no doubt be anxious to try out
FastPrint in a QuickBASIC program. Let's start with a small one which
draws coloured boxes on the screen, just to test that it is working
properly. We'll call it PANELS.BAS.
Start up QuickBASIC with the following ...
QB PANELS.BAS /L DISPLAY.QLB
The first thing to do is to declare our routine as an external
procedure. We won't be expecting it to return any value, so we declare
it as a SUBprogram rather than a FUNCTION.
DECLARE SUB FastPrint (BYVAL Row%, BYVAL Col%, Text$, BYVAL Attr%)
Make sure that all the parameters are correctly listed and of the
correct type. Integers for the Row, Column and Attribute arguments and
a string for the text which we will be supplying.
Okay, was that fast enough for you?
What you have done, in effect, is to add your own extension to the
QuickBASIC language. Provided that the procedure is properly declared
at the top of your program, and the object file is linked or in an
attached Quick Library, you can use FastPrint as easily as any other
QuickBASIC statement or function. You don't even have to preface it
with the CALL directive, nor do you need to enclose the parameter list
with parentheses.
If you are not sure how to work out proper values for the colours and
attributes which you want your display to have, Appendix A goes into
this in some detail. By all means experiment, your taste is very
likely different from mine. You may even want to write special SUBS
The QBNews Page 47
Volume 2, Number 2 May 31, 1991
for commonly-used attributes, for instance ...
SUB RevPrint (Row%, Column%, Text$) STATIC
FastPrint Row%, Column%, Text$, 112
END SUB
... calls FASTPRINT with the attribute set for REVERSE VIDEO text,
Black characters on a White background (actually Green on monochrome
monitors.
SUB NormalPrint (Row%, Column%, Text$) STATIC
FastPrint Row%, Column%, Text$, 7
END SUB
... calls FASTPRINT with the attribute set to normal video. Just like
PRINT in fact, only faster.
By the way, what did all those panels remind you of? That's right,
WINDOWS. Well you'll just have to wait. The Windows will be opened in
a future article.
----------------------------------------------------------------------
ROM-BIOS Video Services
ROM-BIOS provides services which enable us to determine the current
display mode, select the video page to which output will be directed,
find the current cursor position and the character and attribute
underneath it, and to selectively scroll a specified area of the
display. They are accessed by loading the AH register with the number
of the service required, and then issuing interrupt 16 (10 Hex). You
may have to preset values into other registers, depending upon the
service being called.
Unless used for returned values, the contents of the BX, CX and DX
registers are preserved across these calls. Likewise, the segment
registers will be returned intact. You should not assume, however,
that the contents of other registers will be maintained. Remember also
that the system, used by the BIOS for numbering screen rows and
columns, differs from QuickBASIC in that the x/y co-ordinate of the
top-left corner of the screen is regarded as being 0,0 instead of 1,1.
----------------------------------------------------------------------
INT 10h Get current video display mode. all systems
Call with: AH = 0Fh (15 decimal)
Returns: AL = display mode (see table below)
AH = screen width in columns
BH = cureent display page
Mode Resolution Colours Screen Mode Resolution Colours Screen
----------------------------------------------------------------------
0 40 x 25 2 text 1 40 x 25 2 text
2 80 x 25 2 text 3 80 x 25 16 text
4 320 x 200 4 graphics 5 320 x 200 4 graphics
The QBNews Page 48
Volume 2, Number 2 May 31, 1991
6 640 x 200 2 graphics 7 80 x 25 2 text
8 160 x 200 16 graphics 9 320 x 200 16 graphics
10 640 x 200 4 graphics 13 320 x 200 16 graphics
14 640 x 200 16 graphics 15 640 x 350 2 graphics
16 640 x 350 16 graphics 17 640 x 480 2 graphics
18 640 x 480 16 graphics 19 320 x 200 256 graphics
----------------------------------------------------------------------
Computers fitted with the Monochrome Display Adaptor (MDA) will only
support mode 7, (80 x 25 character text).
Modes 4 and 6 correspond to QuickBASIC's SCREEN 1 and SCREEN 2.
Modes 8, 9 and 10 are only available on the IBM PCjr (and Tandy 1000).
Modes 13 and up are only available on machines fitted with EGA or VGA
adaptors, Mode 16 supports either 4 or 16 colours, depending upon the
amount of video memory available.
ROM-BIOS VIDEO SERVICES
----------------------------------------------------------------------
INT 10h Get Font information EGA, VGA, MCGA only
Call with: AH = 11h (17 decimal)
AL = 30h (48 decimal)
BH = 00h for default character set.
Returns: CX = point size (number of bytes per character)
DL = screen height in rows - 1
ES:BP => address of character definition table
Note: information about the alternative character sets
provided by your video adaptor is obtained by loading
BH with the appropriate font number. See your EGA or
VGA reference manual for more details.
----------------------------------------------------------------------
INT 10h Get Configuration information EGA, VGA only
Call with: AH = 12h (18 decimal)
BL = 10h (16 decimal)
Returns: BH = display type (0 = colour, 1 = monochrome)
BL = video memory installed
(0 = 64K, 1 = 128K, 2 = 192K, 3 = 256K)
CX = Adaptor feature bits and DIP switch settings
Notes: BL will still return a value of 3 even if more than
256K of video memory is installed.
for more information about the meaning of the value returned in CX
consult your EGA or VGA reference manual.
----------------------------------------------------------------------
The QBNews Page 49
Volume 2, Number 2 May 31, 1991
Using FastPrint with BASIC 7
If you intend using FastPrint with programs compiled under the BASIC 7
Professional Development System then a few changes are necessary. This
is because BASIC 7 uses Far Strings which gives strings a memory
segment of their own instead of putting them into DGROUP, the default
Data Segment pointed to by DS.
To enable you to find the address of a far string, BASIC 7 provides a
built-in function called StringAddress. This must be declared at the
head of your source code file between the .MODEL and .CODE directives,
so that the Linker can fix-up references to the function when your
program is built:
extrn StringAddress: proc
This done, you should rewrite the lines between labels Fast_04: and
Fast_06: as follows:
Fast_04:
push ax ; Save display co-ordinates
mov ax,[bp+8] ; Index to string descriptor
push ax ; Call BASIC for
call StringAddress ; string parameters
pop bx ; Recover co-ordinates
jcxz Fast_06 ; Abort if a null string
mov ds,dx ; String segment to DS
mov si,ax ; DS:SI==> string data
mov ax,bx ; Co-ordinates to AX
call ScreenAddress ; Calculate target address
mov ax,[bp+6] ; Get display attribute
xchg ah,al ; into AH
cld ; Clear direction flag
Notice that, just like QuickBASIC 4.5, we still have to obtain the
address of the string descriptor from our argument list. With BASIC 7,
however, we pass it on to StringAddress by pushing it back onto the
stack before calling this function. On return, BASIC 7 will have set
our registers as follows:
DX = segment of target string
CX = length of target string
AX = offset of target string
The explanation in the BASIC 7 manual and on-line Help implies that
you need to call a seperate function, StringLength, to find the length
of the string. In practice this is not necessary since StringAddress,
itself, returns this information in CX.
**********************************************************************
Christy Gemmell resides in England and was the major author of the
Waite Group book QuickBASIC Bible. Christy also has a shareware
called the Assembly-Language Toolbox for QuickBASIC. Christy can be
reached in care of this newsletter.
**********************************************************************
The QBNews Page 50
Volume 2, Number 2 May 31, 1991
----------------------------------------------------------------------
P o w e r P r o g r a m m i n g
----------------------------------------------------------------------
An Improved Cloning Algorithm by Larry Stone
Who ever said that there is no such thing as a bug free program
must have been very familiar with my work. To my chagrin, the code I
wrote for "The QBNews", Vol. 1, No. 3, concerning programs that clone
to themselves, contained a bug that could hang your system. In the
endeavor to wipe the egg off of my face, I present a new function that
corrects this error and, as a side benefit, executes faster and, for
those of you who use the PDQ library, is compatible with PDQ.
The function, SearchEXE$, as originally published, used a DO...LOOP
terminated with the instruction, "DO WHILE NOT EOF(1)". In the event
that the program does not contain the special marker in the EXE's
token area (refer to QBNWS103), the loop would execute indefinitely
because the EOF is never read with files opened in the BINARY mode.
The solution is one line of code:
IF Bytes + Portion& >= FiSize& THEN EXIT DO
The other major change presented here is the substitution of GET
for INPUT$(). The GET will operate faster and will make the code
compatible with Crescent's PDQ.
FUNCTION SearchEXE$ (FileName$, Key$, DatLen) STATIC
'The Bytes% variable is the number of bytes to read. If your program is
'less than 16000 bytes then this routine adjusts accordingly. Also, if
'a 16000 byte "GET" cuts the marker in two, you don't have to worry
'about it because we adjust the file pointer accordingly.
Bytes = 16000 '16000 bytes to read with each pass.
BytesToData& = 0 'Initialize it to zero.
Portion& = 1 'Initialize to file's 1st byte.
CountToKey = 0 'The count to Key$ location.
Handle = FREEFILE 'Get a number for the file.
OPEN FileName$ FOR BINARY AS #Handle
FiSize& = LOF(Handle)
DO
'---- 16000 bytes is to many so, reduce it.
IF Bytes > FiSize& THEN Bytes = FiSize&
'---- If Portion& + Bytes is greater than FiSize&, reduce Bytes.
IF Bytes + Portion& > FiSize& THEN Bytes = FiSize& - Portion&
'---- Point into the file (Portion&) and input "Bytes".
A$ = SPACE$(Bytes) 'Configure the string to "Bytes" length.
SEEK #Handle, Portion& 'SEEK to the portion.
The QBNews Page 51
Volume 2, Number 2 May 31, 1991
GET #Handle, , A$ 'Using GET works with PDQ & is faster.
CountToKey = INSTR(A$, Key$) 'Is Key in A$?
IF CountToKey THEN 'Yes, we found the Key!
'---- Calculate were the data is located.
BytesToData& = Portion& + CountToKey + LEN(Key$) - 1
A$ = SPACE$(DatLen) 'Configure the string to DatLen.
SEEK #Handle, BytesToData& 'SEEK to the data location.
GET #Handle, , A$ 'Now get the data.
SearchEXE$ = A$ 'Set the function.
CLOSE #Handle 'Close the file.
A$ = "" 'A little garbage collection
EXIT FUNCTION
END IF
'---- We've reached the end of the file and did not find our
' Marker$. This prevents an endless loop.
IF Bytes + Portion& >= FiSize& THEN EXIT DO
'---- We subtract the key length in case it was split by our GET.
Portion& = Bytes + Portion& - LEN(Key$)
LOOP
CLOSE #Handle
PRINT "Error. Could not find key!"
END
END FUNCTION
The QBNews Page 52
Volume 2, Number 2 May 31, 1991
----------------------------------------------------------------------
N e w a n d N o t e w o r t h y
----------------------------------------------------------------------
Custom Control Factory for Microsoft Visual Basic by Desaware
Desaware has announced the Custom Control Factory version 1.0, a
companion product to Microsoft Visual Basic which permits the user to
create a rich variety of custom button controls without the need for
special programming.
The Custom Control Factory has been developed to bridge the gap
between the standard controls included with Visual Basic and custom
controls which require advanced programming with the Windows Software
Development Kit and Control Development Kit for Visual Basic. Custom
Control Factory buttons are created interactively from within the
Visual Basic design environment without the need for special
programming. A runtime version of the product can be distributed with
compiled Visual Basic applications without further licence fees.
The Custom Control Factory version 1.0, along with over fifty sample
controls, is being introduced at only $48 and will be available
immediately after the release of Visual Basic version 1.0.
You can contact Desaware by calling 408-377-4770.
If you have a new product or an upgrade to an existing product you
would like announced in The QBNews, please send info to:
The QBNews
P.O. Box 507
Sandy Hook, CT 06482
The QBNews Page 53
Volume 2, Number 2 May 31, 1991
----------------------------------------------------------------------
F u n a n d G a m e s
----------------------------------------------------------------------
ASCII Art -- A Piece of Computer History by Charles Graham
In The Beginning ...
folks created ASCII Art. Before ANSPAINT, THEDRAW, GIFs and all
the rest, before color and sound and animation techniques, before CGA,
EGA and VGA, people used COPY CON, EDLIN or their favorite text editor
to produce art similar to the typewriter art of earlier, pre-PC
generations.
Over a number of years, I have collected 12 of these pictures from
bulletin boards and individuals. For all I know, these are the only
examples of ASCII art that remain. Evidently, most ASCII artists were
people who liked to draw full-figured nude women. I have never seen
an ASCII picture of anything but this particular subject. To the best
of my knowledge and belief, all of the pictures that come with this
collection are of unknown origin and are in the public domain.
I have always felt these low-tech creations were worth saving if
only for historic or nostalgic purposes -- like the early efforts of
cave dwellers when they drew stick figures of buffalo on the walls of
their abode.
ASCII art is created by using lower-ASCII characters starting with
ASCII 32 (the blank or space character). To my knowledge, no
upper-ASCII (8th bit on) graphics or foreign language characters were
ever used.
Even using this limited palette, however, viewing and printing the
pictures always presented certain problems. When using the DOS TYPE
command, only a small fraction of the work is on screen at any moment,
and the work is badly out of proportion. Printing them can also be
tricky because the default values of most printers (a horizontal pitch
of 10 characters per inch and a vertical pitch of 6 lines per inch)
produces images that are too long and contain too much white space.
So I created ASCIIART.BAS
What Is ASCIIART.BAS?
ASCIIART solves the on-screen viewing problems by translating the
characters that make up an ASCII picture into color attributes. It
uses these attributes to put pixels on your screen. The resulting
image is perhaps closer to the way the artist originally envisioned
his subject then the DOS TYPE command could ever allow. On EGA
systems, the picture is small but high- resolution and may contain up
to five colors plus black. On CGA systems, the picture is of medium
size and resolution and may contain up to three colors plus black.
ASCIIART does not support monochrome systems.
The QBNews Page 54
Volume 2, Number 2 May 31, 1991
Color Selection
I began the color selection process with what I thought was a
logical assumption: ASCII artists would instinctively choose
characters with more "weight" to denote shading and less "weight" to
denote highlights. I defined "weight" as the amount of ink that hits
the paper when a particular character is printed.
I put my dot matrix printer into draft mode and printed all the
printable characters. I used a magnifying glass and inspected each
one. I counted and recorded the number of dots used to print each
character and divided them into six "weight" groups.
Then through trial and error I began associating different colors
with different "weight" groups until I began to get a pattern that
"worked." Further refinement and tinkering developed the pattern that
comes hard coded in ASCIIART.BAS.
Because different ASCII artists favored different characters to
draw with, the method by which ASCIIART assigns color attributes to
characters is a compromise. However, for the twelve ASCII pictures I
have managed to collect, I believe it is the best compromise available.
Users who want to experiment with color selection can easily do so
by altering the DATA statements at the end of ASCIIART's main module
as well as the FOR/NEXT loops in SUB loadarray. You can come up with
some rather garish renditions without much effort.
Printing The Pictures
ASCIIART also assists you in making a decent looking copy of an
ASCII picture on your printer. The printer parameters that I have
found work best are:
-- Condensed Print, i.e., a horizontal pitch of 17 characters per
inch or close to it
-- Near Letter Quality (NLQ or double-strike) printing as opposed
to Draft Quality printing
-- Uni- as opposed to Bi-directional printing, and
-- a vertical pitch of 7/72nds of an inch, i.e., approximately 10
1/4 lines per inch.
To set these parameters on IBM printers, the following ASCII
characters need to be sent:
-- ASCII 15 (to enter condensed mode)
-- ASCII 27 and 71 (to enter NLQ mode)
-- ASCII 27, 85 and 49 (to enter unidirectional mode)
-- ASCII 27, 65and 7 (to store a vertical pitch of 7/72")
-- ASCII 27 and 50 (to begin using the stored vertical pitch).
To produce the best possible rendition, ASCIIART will automatically
send the correct control codes and escape sequences to supported
printers before printing the pictures. And it allows those who don't
want to send any control codes or escape sequences not to do so.
The QBNews Page 55
Volume 2, Number 2 May 31, 1991
ASCIIART also invites the owners of non-supported printers to enter
their own control codes and escape sequences to set the correct
printer parameters. Entering "C" (for Custom) at the printer
selection screen will allow such users to do this.
Printing Other Pictures
ASCIIART has a feature that will allow you to print or view other
ASCII pictures, should you come across one or care to create one.
Place the picture in the same directory as all the other files
included with this collection and name it OTHER.ASC. Then, when you
are prompted for the name of the picture, enter the name OTHER. You
can even view your word processing documents this way -- if you'd like!
In Closing
To help keep this edition of The QBNews at a reasonable size, at
the Editor's request only 2 of the 12 available ASCII pictures have
been included. The full set of 12 may be downloaded as ASCIIPIC.ZIP
on the Crescent Support BBS at no cost. If calling Crescent is
inconvenient, the same set is available from the author on a 360K
diskette for $3.00 to cover the cost of materials, postage and
handling.
I find ASCII art to be quaint and entertaining. If you come across
or create any good ASCII pictures I'd appreciate your sending me a
copy.
Enjoy!
*********************************************************************
Charles Graham is a division head for a local government agency in St.
Louis County, Missouri. He also teaches QuickBASIC part time at a
local community college. He is the author of several shareware
products including MOVIES . ON . LINE and Quick Dial. He can be
contacted at Post Office Box 58634, St. Louis, MO 63158, and on the
National QuickBASIC Conference.
*********************************************************************
The QBNews Page 56
Volume 2, Number 2 May 31, 1991
----------------------------------------------------------------------
Q B N e w s S p e c i a l R e p o r t
----------------------------------------------------------------------
Special Preview of Visual Basic by Dave Cleary
On May 20, 1991, Microsoft announced the long anticipated Visual
Basic Windows Development Environment. Steve Gibson calls it the
"masterstroke of the decade". Stewart Alsop calls it "the perfect
user programming environment - for the 1990s". But what does Visual
Basic mean to me and you, the QuickBASIC developer?
First, I'm going to start out telling you what Visual Basic is and
isn't. Visual Basic isn't an upgrade to QuickBASIC. VB is a whole new
product that requires all new tools and a totally different thought
process in regards to designing your programs. This is because VB
creates Microsoft Windows programs and writing Windows programs is
like write for a whole new operating system. Instead of calling DOS
to perform needed functions, you call the Windows API (Application
Programming Interface).
Windows is an Event Driven environment. This is why writing
programs in VB requires a different thought process. When you create
a VB program, the majority of your subs and functions will not be
invoked by you. Instead, Windows will invoke them depending on
actions performed by the user. We will go deeper into event driven
programming later in the article.
The Visual Basic environment isn't as sophisticated as the
QuickBASIC environment. Although VB does contain many features of the
QB environment, some are left out. The biggest omission is the fact
you can not set up a watchpoint on a variable. Instead, VB allows you
to go the an immediate window to view or set variable values. VB also
lets you insert Debug.Print statements throughout your code to print
values to your immediate window as the program runs. VB does contain
QB's threaded p-code technology allowing you to change code on the
fly without a recompile. The amusing thing is that C and Pascal
programmers who try VB think it is the greatest thing since sliced
bread. I feel, however, that QB programmers who try VB will be a
little disappointed - at first.
VB is probably the easiest and most productive Windows development
environment around. For instance, I can create a Windows file manager
that contains a drive list, a directory list, and a file list, and
give the user three push buttons with the choice of editing, running,
or deleting the selected file. All this can be done with about ten
lines of code. Of course, creating sophisticated programs still
require a good amount of work and skill by the programmer.
Creating programs with VB is a two step process. The first step is
to create your user interface. This is done with VB's visual form
designer and a collection of controls. A control is an object like a
push button, a text box, or a timer event. VB comes with 15 such
controls, but VB also lets you add custom controls. This effectively
lets you extend the VB environment. To create a custom control, you
need to use either C or ASM and the Windows SDK. However, numerous
third party vendors will be selling libraries of custom controls.
The QBNews Page 57
Volume 2, Number 2 May 31, 1991
A control has properties, events and methods. A property is a
physical characteristic of the control. For instance, the text in a
push button is a property of that control. You are allowed to set and
read properties of controls when you create them (design time), and
when you execute your program (run time).
After you are down drawing your user interface a setting up your
default control properties, you are ready to start coding. The big
difference is that most of your code will be associated with a
control event. An event is basically an action by the user that has a
subprogram associated with it. This is how you program for Windows
event driven environment. For instance, when you draw a push button
on the screen, certain events are associated with it. One of these
events is when the user clicks the button. VB creates a procedure
called Command1_Click. Command1 is the default name given to the
first push button you draw, but you can change it to whatever you
like by setting its control name property. The code you put in this
sub is executed every time a user clicks the button.
Finally, controls have methods associated with them. A method can
be thought of as an executable statement of the control. For
instance, VB has a List Box control. You add items to the list box by
using the AddItem method.
If you aren't totally confused by now, then I'm suprised. The
above is not meant to teach you how to program in VB, but rather to
show you how different it is. This is why I feel porting existing QB
programs over to VB will be difficult at best. Now the question is
"Should I buy VB?". If you plan on doing any Windows development at
all, then the answer is a resounding yes. If you don't plan on
writing any Windows programs, but use Windows as your environment of
choice, then the answer is still a resounding yes. You will be able
to create those little utilities you wished you had, but haven't
found yet and you will be able to do it in short order. If you hate
Windows, or don't own an 80286 or 80386 machine, then don't bother.
You won't be able to use VB anyway.
Registered users of any Microsoft BASIC product can buy VB from
Microsoft for $99, half off list price. You can call Microsoft at
1-800-426-9400 or wait for a coupon to appear in your mail. Also, I
have started a Windows BASIC conference on Fidonet. This conference
is for the discussion of all Windows BASIC products, not just VB.
Sysops interested in linking up to this conference should contact me
at 1:141/777. Also, if you are interested in checking out some actual
VB applications, you can give my BBS a call at 203-426-5958.
*********************************************************************
Dave Cleary is an electronics engineer for Vectron Labs in Norwalk,
CT. Besides being a Visual Basic beta tester, Dave is also the author
of Crescent Software's PDQComm and is the publisher of this
newsletter. He can be contacted on Compuserve at 76510,1725, on
Prodigy at HSRW18A, and on Fidonet at 1:141/777.
*********************************************************************
The QBNews Page 58
----------------------------------------------------------------------
E O F
----------------------------------------------------------------------
Receiving The QBNews
The QBNews is distributed mainly through BBS systems around the
world. Some of the networks it gets distributed through are SDS
(Software Distribution System) and PDN (Programmers Distribution
Network). Ask the sysop of your local board about these networks to
see if there is a node in your area.
The QBNews can also be found on CompuServe in the MSLang
(Microsoft Language) forum. It can be found in file area 1 or 2 of
that forum. Just search for the keyword QBNEWS. The QBNews will also
be available on PC-Link. I send them to Steve Craver, who is the BASIC
Programming Forum Host on PC-LINK and he will make them available. I
would appreciate anybody who could upload The QBNews to other services
such as GENIE since I don't have access to these.
I have also set up a high speed distribution network for people
who would like to download The QBNews at 9600 baud. The following
boards allow first time callers download privileges also. They are:
Name Sysop Location Number Node #
---------------------------------------------------------------------
Treasure Island Don Dawson Danbury, CT 203-791-8532 1:141/730
Gulf Coast BBS Jim Brewer New PortRichey,FL 813-856-7926 1:3619/20
221B Baker St. James Young Panama City,FL 904-871-6536 1:3608/1
EMC/80 Jim Harre St. Louis, MO 314-843-0001 1:100/555
Apple Capitol BBS Bob Finley Wenatchee, WA 509-663-3618 1:344/61
Finally, you can download The QBNews from these vendors BBS's:
The Crescent Software Support BBS 203-426-5958
The Microhelp BUG BBS 404-552-0567
404-594-9625
You do not have to be a customer of these vendors in order to download
The QBNews, but the Microhelp BBS only allows non-members 15 minutes
of time per call.
If you would like to receive The QBNews on disk, I offer a yearly
subscription for $15.00. This includes four disks containing each
issue as it is published. If you would like a disk with all the back
issues of The QBNews, please enclose an additional $5.00. The pricing
The QBNews Page 59
Volume 2, Number 2 May 31, 1991
structure is as follows:
Base Price for 1 Year - $15.00
Disk with Back Issues - $5.00
3.5" disk surcharge - $5.00
Canada and Mexico surcharge - $5.00
All other foreign orders - $10.00
The base price includes 5.25" 360k disks. Send a check or money
order in U.S. funds to:
The QBNews
P.O. Box 507
Sandy Hook, CT 06482
Please be sure to specify what archive format you want. The
QBNews normally uses PKZip as it's archiver.
----------------------------------------------------------------------
Submitting Articles to The QBNews
The QBNews relies on it's readers to submit articles. If you are
interested in submitting an article, please send a disk of Ascii text
of no more than 70 characters per line to:
The QBNews
P.O. Box 507
Sandy Hook, CT 06482
Articles can also be submitted via E-Mail. Send them via Compuserve to
76510,1725 or via FidoNet to 1:141/777. I can be reached at the above
addresses as well as on Prodigy as HSRW18A.
The QBNews Page 60